Skip to content

fix(akita)!: eliminate redundant opening framing and rehashing - #1842

Open
quangvdao wants to merge 5 commits into
mainfrom
codex/akita-lean-opening-wire
Open

fix(akita)!: eliminate redundant opening framing and rehashing#1842
quangvdao wants to merge 5 commits into
mainfrom
codex/akita-lean-opening-wire

Conversation

@quangvdao

@quangvdao quangvdao commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR makes Jolt's Akita opening envelope minimal and gives transcript binding and proof decoding one canonical owner each.

It now:

  • carries exactly a fixed 32-byte schedule-row digest plus Akita's headerless backend proof body;
  • derives the Jolt-to-Akita bridge from the complete outer opening statement instead of copying that challenge into the proof;
  • places the bridge in Akita's session label, where it survives concrete-instance binding;
  • derives the canonical backend proof shape from the trusted resolved schedule and opening layout before decoding; and
  • ends outer Jolt challenge derivation at the bridge instead of rehashing the complete serialized Akita proof after the final challenge.

The Akita backend proof remains fully verified. This removes only derivable wire metadata and challenge-dead outer transcript work; the nested Akita transcript still binds the concrete instance and every proof message required by its Fiat–Shamir schedule.

Diff metadata

  • Base: 959686da3d72e0cb69f8102eb8e2f30af44034ff (main merge base)
  • Head: 1efb96ce64079277f83fc9f6fefd236922e99417
  • Commits: 5
  • Files changed: 7
  • Diff: 72 insertions, 239 deletions

Motivation

Before this cut, AkitaBatchProof wrapped Akita's already-canonical proof body in three additional prover-controlled values:

  1. a copied Jolt statement challenge;
  2. a variable-length serialized schedule selection; and
  3. a serialized backend proof-shape descriptor.

All three were redundant.

  • Jolt already derives the bridge challenge after absorbing the verifier setup and complete opening statement.
  • The schedule selection has a fixed 32-byte canonical identity.
  • Once the verifier resolves that identity under its configured catalog, the schedule and public opening layout determine the grinding plan and exact backend proof shape.

The old representation therefore spent proof bytes on verifier-derivable data and let the proof provide metadata that controlled backend decoding before the verifier compared it with its own expected shape.

There was a second, independent redundancy at the transcript boundary. After Jolt derived the bridge—the final outer challenge—the adapter appended the complete serialized Akita opening back into the outer Jolt transcript. No later Jolt challenge consumed that state. Akita separately decoded the body and absorbed its semantic proof messages into the nested Akita transcript, where those messages actually determine challenges and acceptance. Rehashing the raw body in the outer transcript could not affect verification, but it imposed avoidable native work and would be especially expensive for a recursive or circuit transcript replay.

Architecture

flowchart LR
    A[Jolt verifier setup and opening statement] --> B[Jolt transcript]
    B -->|one canonical Fp128 bridge| C[Akita session label]
    C --> D[Akita instance and semantic proof transcript]
    D --> E[Akita verification]

    F[Wire: 32-byte row digest and backend body] --> G[Resolve trusted schedule]
    G --> H[Derive canonical shape and decode budget]
    H --> D
Loading

The outer-to-inner handoff is one-way for this terminal opening stage:

  1. Jolt absorbs the setup and public opening statement.
  2. Jolt samples one canonical Fp128 bridge challenge.
  3. The bridge bytes extend the Akita session label.
  4. Akita binds its concrete instance and runs its own Fiat–Shamir protocol.
  5. Jolt does not reabsorb the raw Akita proof because no later outer challenge exists.

If a future protocol adds an outer challenge after Akita verification, that protocol will need an explicit compact inner-transcript receipt. Rehashing the transport encoding is not retained speculatively for that currently nonexistent continuation.

Change-surface overview

Area Before After
Proof envelope Four variable-length byte vectors Fixed [u8; 32] schedule identity plus one backend Vec<u8>
Statement bridge 16-byte field challenge copied into the proof and compared by the verifier Derived independently and incorporated into the Akita session label; zero proof bytes
Schedule selection Length-prefixed serialized wrapper Fixed-width row digest reconstructed as OpeningScheduleSelection
Proof shape Prover-supplied serialized descriptor, capped and compared with the expected shape Derived only from the trusted resolved schedule, grinding plan, and public opening layout
Decode authority Proof supplied both the body and its claimed shape Proof supplies only body bytes; verifier supplies shape and byte budget
Outer transcript Complete Akita opening appended after the final outer challenge Challenge derivation ends at the bridge; raw proof is not rehashed
Nested transcript Fresh Akita state plus an appended bridge field Bridge is part of the session label preserved across instance binding

Lean proof envelope

AkitaBatchProof now contains exactly:

pub struct AkitaBatchProof {
    schedule_selection: [u8; 32],
    backend_proof: Vec<u8>,
}

The fixed array makes malformed selection lengths unrepresentable after deserialization. selection() reconstructs the public OpeningScheduleSelection directly from the digest, while backend_proof_body_size() and unframed_payload_size() report the remaining wire components without mirroring the backend shape.

For the forced-K=256 release sample measured at fdc9e1976—whose wire representation is unchanged by the later transcript-only commits—the resulting encoding was:

  • complete Jolt proof: 85,219 bytes;
  • Akita opening wire: 70,339 bytes;
  • Akita backend proof body: 70,302 bytes;
  • required schedule identity: 32 bytes; and
  • enclosing Jolt/bincode Vec<u8> boundary: 5 bytes.

Thus 70,302 + 32 + 5 = 70,339: the Akita opening contains no copied challenge or proof-shape blob. Removing the final five serializer bytes would require changing Jolt's enclosing proof codec and is out of scope.

Verifier-owned shape and bounded decoding

The verifier now owns the entire decode shape:

  1. reconstruct the fixed-width schedule selection;
  2. resolve it under the configured trusted schedule catalog;
  3. construct the public OpeningClaimsLayout;
  4. derive the schedule's transcript grinding plan;
  5. derive the canonical AkitaBatchedProofShape;
  6. validate the body length against that shape's decode budget; and
  7. decode the backend proof with the derived shape, rejecting trailing bytes.

The old 16 KiB cap on a prover-controlled shape blob disappears because that blob no longer exists. Commitment coefficient counts and serialized commitment lengths remain checked before shape-backed allocations. Malformed inputs continue to return OpeningsError rather than reaching unchecked allocation or indexing paths.

Cross-protocol transcript binding

The bridge remains one Jolt challenge_scalar() sampled at the same point in both grouped and single-group opening flows. Its canonical 16-byte representation contains the full entropy of the approximately 128-bit Akita base-field element; a 32-byte container would not add entropy.

The important change is where those bytes live. Akita replaces its sponge state when it binds the concrete instance but preserves its session label, so the adapter constructs:

AkitaTranscript::new(akita_domain || canonical_bridge_bytes)

This makes the nested Akita proof depend on everything the outer Jolt transcript observed before the bridge without transporting a proof-controlled copy.

The complete AppendToTranscript for AkitaBatchProof implementation and all four prover/verifier append calls are removed. The Fiat–Shamir inventory records:

  • the same single outer bridge challenge, renamed at its new owner;
  • no raw schedule-selection or backend-proof absorption through AkitaBatchProof; and
  • no terminal proof append in either verifier path.

The backend body is still decoded and verified by Akita. Corrupting either the schedule identity or the backend body continues to reject; acceptance never depended on the challenge-dead outer append.

Security and verifier safety

  • The Jolt statement and setup determine the bridge before the prover can influence any nested Akita challenge.
  • The bridge is retained through Akita instance binding by the session label.
  • The proof cannot choose its decode shape, grinding width, row counts, or allocation geometry.
  • The same trusted resolved schedule drives both canonical shape derivation and backend verification.
  • Fixed-width schedule identity removes a variable-length parsing surface.
  • Backend-body decode budgets and commitment coefficient counts are checked before allocation-heavy decoding.
  • Unknown serialized fields, corrupted bodies, invalid schedule selections, forged commitment metadata, statement/layout mutations, and trailing bytes reject.
  • Removing the terminal outer append changes no challenge value: no subsequent outer squeeze exists.

Breaking changes

This intentionally changes the serialized Akita opening representation and nested transcript initialization.

Previously serialized Akita-mode Jolt proofs do not decode or verify under this representation. The removed fields are not accepted through aliases, compatibility wrappers, or an alternate decoder.

The following remain supported:

  • dense and one-hot Akita openings;
  • grouped trace openings and ordinary single-group batching;
  • K=16 and forced-K=256 one-hot schedules;
  • advice and full-advice proofs;
  • committed-program proofs; and
  • existing checked malformed-input rejection at the verifier boundary.

The native Akita backend proof encoding itself is unchanged.

Commit map

  • fdc9e1976 — replace the redundant envelope, derive the canonical proof shape verifier-side, and bind the bridge through the Akita session label;
  • 6bd109b9f — refresh the reviewed Fiat–Shamir challenge and absorption inventory;
  • b45c1ec79 — remove challenge-dead outer absorption of the terminal Akita opening; and
  • ef77d826a — document the exact terminal challenge boundary; and
  • 1efb96ce6 — merge current main (959686da3) while preserving the seven-file PR surface.

Validation

Completed locally for the behavior change at b45c1ec797037da1ac76f4fe85c143845627c34c:

  • cargo nextest run --release -p jolt-akita --cargo-quiet54 passed, 1 skipped;
  • cargo nextest run --cargo-profile ci -p jolt-prover --features akita,prover-fixtures --test akita_e2e --cargo-quiet6 passed;
  • Akita and Akita-enabled Jolt prover Clippy targets with warnings denied;
  • formatting and diff checks.

Repeated on ef77d826a05fc4e960f6e2276652966e09b1f2a6 after the documentation-only follow-up:

  • cargo nextest run -p jolt-verifier --test fs_obligations --features fs-audit --cargo-quiet1 passed;
  • cargo clippy -p jolt-akita --all-targets -- -D warnings;
  • cargo fmt --check;
  • git diff --check.

After merging current main, final head 1efb96ce64079277f83fc9f6fefd236922e99417 preserves the same seven-file, 72-insertion/239-deletion PR diff. The Fiat–Shamir obligation test, Akita Clippy target, formatting check, and diff check were repeated successfully on that exact head.

Fresh GitHub checks for 1efb96ce6 are running. No current-head failure was observed when this description was published; prior-head green results are not presented as final-head CI evidence.

Remaining work and deliberate non-goals

  • Akita should eventually expose a first-class external transcript-binding/domain-separation API so Jolt does not need to construct the bridged session label directly. That API can preserve this zero-byte bridge representation.
  • This PR does not compress or otherwise change Akita's canonical backend proof body.
  • This PR does not change Jolt's general enclosing proof codec to recover the remaining five bincode bytes.
  • No compact inner-transcript receipt is added because the current Jolt protocol has no outer challenge after terminal Akita verification.

Reviewer map

Suggested review order:

  1. crates/jolt-akita/src/adapters.rs — final proof envelope and the outer-to-inner bridge owner;
  2. crates/jolt-akita/src/native_batching.rs — grouped and single-group prover/verifier transcript flow;
  3. crates/jolt-akita/src/shape_guard.rs — trusted shape derivation, decode budget, and malformed-input boundary;
  4. crates/jolt-verifier/tests/fs_inventory/ — exact challenge preservation and deleted terminal absorptions;
  5. crates/jolt-akita/tests/pathologies.rs — corrupted payload and forged commitment-metadata rejection; and
  6. crates/jolt-prover/tests/akita_e2e.rs — exact lean-wire assertion and end-to-end schedule tampering.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

📏 PR diff

Note

Actual code changed: 180 lines

🟢 +54 added    🔴 −126 removed

Tests, docs, fixtures, and helper scripts excluded.

Category 🟢 Added 🔴 Removed
⌨️ Actual code +54 −126
🧪 Tests +11 −108
📚 Docs +7 −5
📦 Fixtures 0 0
🔧 Helper scripts 0 0
Total diff +72 −239

Every changed line is classified once.

@github-actions github-actions Bot added the no-spec PR has no spec file label Sep 3, 2026
@quangvdao quangvdao changed the title fix(akita)!: remove redundant opening payload fix(akita)!: eliminate redundant opening framing and rehashing Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-spec PR has no spec file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant