Repository navigation
Conversation
Contributor
📏 PR diffNote Actual code changed: 615 lines🟢 +310 added 🔴 −305 removed Tests, docs, fixtures, and helper scripts excluded.
Every changed line is classified once. |
quangvdao
added this pull request to stack #2112
October 21, 2026 15:50
4 tasks done
…elete the legacy facade
…ndFold over the spongefish transcript
…cript Bind the full statement as the spongefish instance, absorb commitments as labeled messages, carry the NARG string in JoltProof and check its end, and move per-round grinding onto the shared SpongePow core.
The fs-audit transcript is the production verifier state with a recording script that also replays proof-of-work check bytes, so frozen replays over changed inputs model a verifier whose every squeeze is fixed.
… the spongefish transcript Keccak is the protocol sponge, Blake2b512 behind transcript-blake2b; the Poseidon protocol selection is removed until the transcript absorbs native Fr.
jolt-prover names Fr challenges, so its transcript dependency enables bn254 itself instead of relying on feature unification. Drop an unused import in the field-inline Akita fixtures and the SDK, and avoid unwrap in the zk statistical test.
- An unlabelled UnivariatePoly round absorbs as a labelled sum-check round with its coefficient count, so a longer polynomial cannot absorb like a shorter one. - Document that every absorbed Encoding must decode uniquely, since the sponge absorbs bytes without framing. - Correct the prover-grinding comment: crates holding both sides enable yolocrypto in verifier builds; it changes visibility, not bytes. - List precommitted as unbound in the statement test, and check that every unbound edit changes the inputs. - Fix two test ports: count challenge draws rather than PoW squeezes, and squeeze the prover's tail once in the grouped-capacity loop. - Re-bless the absorb-site inventory.
Import types and traits by short name on lines this branch adds, as check_style_invariants requires, apply rustfmt, and commit the lockfiles, verifier fixtures and fuzz seeds regenerated for the new transcript bytes.
Rename the order test's label to a dictionary word of the same length, and drop jolt-sumcheck's num-traits dev-dependency, whose only user was the deleted mersenne61 compatibility test.
The proof-of-work nonce search copies the sponge state, which spongefish 0.8.0 exposes only under `yolocrypto`. jolt-sumcheck and jolt-blindfold turned `prover-grinding` on in every build, so jolt-verifier compiled the search and `yolocrypto` too. - jolt-sumcheck and jolt-blindfold gain a `prover` feature holding their prove side: the round loop, `grind_round`, the recorders' rounds, the committed builder and BlindFold's `prove`. jolt-prover, jolt-kernels and jolt-spartan-prover enable it; jolt-verifier's graph has neither the search nor `yolocrypto`. - jolt-transcript's default features are Keccak only. Blake2b and Poseidon are opt-in; the fuzz workspaces that use them, or BN254 challenges, now name those features. - CI lints the verifier-only graph, checks jolt-verifier's feature tree, and tests jolt-sumcheck and jolt-blindfold with their prover.
…ge-6b draw site Stage 6b now draws through J6's one `sequence`, so its three further `challenge` sites fold into one. Also rustfmt the stage-6b draw test.
Pin all Akita crates to the LayerZero-Labs/akita#232 head, which raises the Module-SIS gate from 128 bits to an ADPS16 quantum Core-SVP score of at least 169 bits (NIST category 3) and doubles the compression ladder's terminal image. It also carries Akita main through #189. The commitment shape guard now derives the expected payload length from GroupCommitPhaseParams::outer_compression_plan, Akita's single owner of the B-image compression chain and its ladder admission rule. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 23f1cd2)
K=16 trace keys now cover log_T 12..=31 (arities 16..=35). The admitted one- and two-polynomial scalar range reaches arity 44 for both K values, and the dense producer grid reaches 35. The K=256 Single catalog also carries the narrowest and widest native traces, derived from the same RV64 width enumeration as K=16, at 2^30 and 2^31. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 74c695a)
…licy gen_jolt_schedules at the pinned Akita revision replans every base and fixture catalog under the 169-bit SIS policy and the 2^31 coverage grid. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 76192a0)
Extend the trace-geometry and field-inline coverage tests to 2^31, and add an ignored check that plans every Jolt-scale key of the generator's family specs and reports levels, estimated proof bytes, and setup capacity. The cutover test now states the rule for every emitted trace key in every packaged profile: rows below the cutover are direct, and Single rows at or above it offload. The regenerated one-column Single rows at arities 16 and 17 terminate immediately and the 25-variable one-column row stays direct, so the grouped capacity and serde-transport fixtures move to shapes that still model their cases (18 and the 51-column cutover trace), and the small-arity rejection test pins the new 12..=17 range. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit d434e02)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 5a46b84)
LayerZero-Labs/akita#232 merged as dfdbd767 on Akita main. Pin every Akita crate to that commit instead of the PR head. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit df72e56)
K=256 catalogs are fixtures only, so drop the 2^30/2^31 K=256 trace rows, which no test or fixture consumed, and keep the admitted scalar ceilings at 40 and 43: the 2^31 trace arities (35 for K=16, 39 for K=256) already fit, and nothing uses wider scalar rows. Remove the ignored Jolt-scale planning diagnostic; gen_jolt_schedules --check already replans every key and compares the artifacts byte for byte. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit c862f4a)
DIRECT_PROGRAM_MAX_PHYSICAL_VARS still rejects 35-variable committed programs; the new dense rows do not change that admission guard. Also drop an arithmetic-only fixture comment. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit adb9a79)
Stage 0 absorbs each Akita commitment's metadata and its group's 32-byte digest. Stage 8 runs Akita's embedded prover and verifier on Jolt's own transcript state, and verification ends with Akita's end-of-proof check. This removes the second Akita transcript (bridged_akita_session), the setup absorb (append_verifier_setup), the duplicate statement absorbs and shape_guard.rs. The commitment wire now carries the group's canonical bytes. Other changes: - pin Akita to LayerZero-Labs/akita#254 (bbba19bf4) - add jolt-openings::GroupBatchOpening for the stage-8 batch API - move the Akita program-preprocessing digest domains to v2 and re-pin the goldens - re-bless the FS inventories; the scanner records transcript hand-offs - add a full-verifier argument-string tamper test - update the jolt-akita scheme tests to the new binding - refresh the fuzz lockfiles
…d Jolt metadata The standalone BatchOpeningScheme and CommitmentScheme paths ran Akita on a fixed session, so a valid opening verified under a different caller transcript, setup default layout, commitment layout digest or catalog. The session label now binds one challenge from the caller's transcript, both layout digests and the trusted catalog digest. J7's catalog-replay and direct-opening rejection tests are restored unchanged. The embedded stage-8 path is unaffected. Two tests that CI's fail-fast hid also needed the regenerated catalogs: a single one-hot polynomial now first gets a recursive child fold at 18 variables, so grouped_capacity uses an 18-variable trace with 24-variable advice and the scalar-guide unit test pins the new boundary. The commitment round-trip test compares re-encoded bytes, since the decoded ring vector is in compact mode.
A frozen replay replaced every challenge draw but let the sponge absorb the mutated messages. Akita's embedded opening draws from that sponge directly, so a frozen verifier rejected at stage 8 and the two Akita replay tests had to accept that rejection. The audit script now records the bytes of every absorbed message and a replay absorbs the recorded bytes instead, so the sponge stays the honest one. Both Akita replay tests again require the frozen verifier to accept.
The embedded change replaced the commitment wire with the committed group's self-describing bytes and dropped shape_guard. Decoding those bytes read the row count from the prover's header and could reserve about 1 GiB before any trusted check, and the wire grew by the group's profile. Restore main's wire: Jolt's metadata, the coefficient count, and the serialized rows. The profile stays off the wire; the verifier resolves it from the trusted schedule row. Stage 0 absorbs the metadata and the rows, as on main. - restore shape_guard from #2107, adapted to Akita's fallible CommittedGroup::new; both stage-8 paths decode through it again - restore AkitaProverHint { commitment, backend, source } and main's program-preprocessing digest domains and goldens - restore akita_forged_commitment_metadata_rejects_before_shape_backed_allocation - pin the same guard on the embedded trace batch, and pin that a verifier setup with a different shape, layout digest or K rejects the proof - restore #2107's serde-transport test, including its setup-prefix check
#254's new head re-pins Akita's own jolt crates to J7's rebased head and updates a spec; Akita's code is unchanged.
One field element of about 127 bits lets two caller states collide for about 2^64 hash calls. Two elements put that at the 2^128 target.
…ace batch Both embedded batch tests again check that the prover's and verifier's sponges agree after the opening, squeezed before check_eof. The streamed test also checks that a trace-batch proof carrying standalone backend bytes is rejected.
Jolt's statement, preprocessing digest included, is absorbed when the transcript starts, not through a message. Replays now pass it through the script as well, so a frozen verifier's sponge stays honest from its first absorb and Akita's embedded opening still draws honest challenges.
The scripted transcript now passes absorbed messages and the initial statement through its script, and the standalone Akita session draws its caller challenges with challenge_vector.
That head merges #253's latest commit, which adds a compile-time check that Akita's transcript security target equals Jolt's.
quangvdao
force-pushed
the
transcript/j8-akita-embedded
branch
from
October 21, 2026 21:25
933024d to
ed90d31
Compare
J7 was rebuilt on main, which now carries #2107 and J6, so J7's old head d04df1b is no longer its ancestor. This merge keeps J7's tree and re-applies J8's own change on top: Akita's opening inside Jolt's sponge, main's commitment wire behind #2107's shape guard, the standalone session binding, and the FS replay freezing. The #2107 cherry-picks on this branch are superseded by main's squash of #2107. Dory keeps J7's label-based short challenges. The only lockfile conflict was resolved by taking J7's lock and re-resolving the Akita pin.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked, not mergeable yet.
6cea4bc85). J7 was rebuilt on main, which now carries feat(akita)!: NIST category 3 Module-SIS and 2^31 schedule coverage #2107 and J6, so J8 merges J7's new head instead of rebasing (4f61f4e6e, thena87225028for J713e149195). The feat(akita)!: NIST category 3 Module-SIS and 2^31 schedule coverage #2107 cherry-picks in this branch's history are superseded by main's squash of feat(akita)!: NIST category 3 Module-SIS and 2^31 schedule coverage #2107. Dory keeps J7's label-based short challenges.c16468ac6, which keeps Akita at protocol v8 (one version bump per stack, counted frommain). That commit is not merged; it is stacked on Optimize read-write memory commitments #253 and includes Optimize read-write memory commitments #253's headdac235470. After the Akita PRs merge, re-pin to the merged commit.What this does
Akita's opening now runs inside Jolt's single Fiat-Shamir sponge, so each message is absorbed once. Before, Jolt absorbed Akita's setup and statement into its own transcript. It then started a second Akita transcript, which absorbed them again.
Stage 0 (unchanged from main). For each Akita commitment, Jolt absorbs:
The wire is main's
AkitaCommitment; this PR does not change it.Stage 8.
batched_prove_embeddedon Jolt's prover state.batched_verify_embeddedon Jolt's verifier state, which reads Akita's messages out of Jolt's argument string.Decoding. Before decoding a commitment's rows, the verifier runs feat(akita)!: NIST category 3 Module-SIS and 2^31 schedule coverage #2107's
shape_guard. The guard resolves the selected schedule row from the trusted catalog. It then checks the declared coefficient count and the byte length against that row's profile (outer_compression_plan().terminal_coefficients()). So a forged header cannot make the decoder allocate a payload-sized buffer.End of proof.
verifyends withPCS::check_eof, which rejects trailing argument-string bytes. The trace batch's proof must carry an emptybackend_proof, because Akita's argument now lives in Jolt's argument string.The stage-8 API is a new trait,
jolt_openings::GroupBatchOpening, withSponge,PendingEof,prove_batch,verify_batchandcheck_eof.Removed
bridged_akita_session: the second Akita transcript and its session handshake.append_verifier_setup: setup dimensions, chunk profile, layout and catalog digest absorbed into Jolt's transcript.append_batch_statementand thebind_*_statement_transcriptshelpers: the statement absorbed twice.The standalone path
The generic
BatchOpeningSchemepath and the single-polynomial path still run Akita on a fresh Akita session. Only tests, benches and the ZK stub use them. The session label (standalone_session) binds what Akita's own descriptor does not:All parts have fixed lengths. The embedded stage-8 path does not use this label.
The regression in the earlier head, and the fix
The earlier head of this PR (
933024d97) had three problems.shape_guard, so Akita's self-describing decoder ran before any trusted-length check. A forged header could make the verifier reserve up to about 1 GiB before rejecting. That reaches the production verifier, at stage 8.bridged_akita_sessionleft the standalone session without the caller's transcript state and Jolt's layout and catalog digests. J7's catalog-replay and opening-layout tests caught this. The production verifier was not affected: it only uses the embedded path, where Akita's descriptor binds the selected row and the layout, and stage 0 binds the commitments.The fix:
shape_guardbefore decoding, on both the embedded and the standalone path.standalone_sessionbinds the caller state and the digests.frozen.output.is_ok()holds again.Sizes
Bincode sizes of the two Akita fixture proofs. "main" is a16z/jolt main (
5aa807b03). "#2107" is its head (adb9a79c8), which adds Akita #232's category-3 SIS policy. "J8" is this PR. J8's opening counts the 33-bytejoint_opening_proofplus Akita's bytes in Jolt's argument string.joint_opening_proofkeeps only the 33-byte schedule selection. Akita's 74,902 (muldiv) and 80,695 (advice) bytes now live in Jolt's argument string.Can the verifier derive the commitment metadata?
Each field, and where the verifier could get it instead of reading it from the commitment:
num_vars: from the opening point's length.poly_count: from the number of claims in the statement.one_hot_k: from the verifier setup.backend_coeff_len: from the trusted profile of the selected schedule row (outer_compression_plan).layout_digest: for the trace group, fromsetup.default_layout_digest. Advice and program commitments choose their layout when they are committed, so the verifier cannot derive their digest without a per-object source.This PR does not change these fields.
Tests
Restored to base
akita_forged_commitment_metadata_rejects_before_shape_backed_allocation(tests/pathologies.rs).fs_attacksreplay tests withfrozen.output.is_ok().scheme.rs).expect_errs.New
akita_argument_string_tampers_reject. It checks that an appended argument-string byte givesNargNotConsumed, and that a flipped byte or a truncated string rejects.standalone_session_binds_jolt_metadata_and_the_caller_state. It checks that the session changes with the caller's transcript, either layout digest, and the flavor.max_num_vars, polynomial bound, default layout digest or one-hot K;backend_coeff_len(rejected by the shape guard, with the message matched);backend_proof.FS harness
Script::absorb_byteslets the test-onlyScriptedTranscriptsubstitute absorbed bytes. The audit harness records every absorbed message and replays it, so a frozen verifier's sponge matches the honest one.batched_verify_embedded,verify_batch,check_eof).Test-diff audit
Every assertion removed, loosened or rewritten against J7 and main:
setup_key_transcript_binds_backend_shape(removed). It checked that Jolt's transcript absorbed the backend setup shape, andappend_verifier_setupis gone. Its replacements:standalone_sessionbinds the layout and catalog digests.max_num_varsand one-hot K are checked for exact equality.check_eof.akita_field_inline_e2e: flip abackend_proofbyte → flip an argument-string byte (rewritten).backend_proofis now always empty, and the new streamed check covers a non-empty one.Soundness re-check
A read-only review of this PR's diff found all seven questions sound:
test-utils, and its identity encoding absorbs exactly the original message.Two notes:
BatchOpeningSchemedocs already leave binding the statement to the caller.Validation
All on a Linux sandbox with CI's commands (
cargo nextest run --cargo-profile ci); every run passed:-p jolt-transcript, and-p jolt-transcript --all-features-p jolt-akita, with and withoutfield-inline-p jolt-verifier --test fs_attacks, withakita,fs-audit,prover-fixturesand withfs-audit,prover-fixtures-p jolt-verifier --test fs_obligations, withfs-auditand withfs-audit,akita-p jolt-verifier --features akita,prover-fixtures --test-threads 1(84 tests)-p jolt-prover --features akita,prover-fixtures(40 tests)-p jolt-prover --features prover-fixtures,field-inline,akita, includingakita_field_inline_e2e(37 tests)-p jolt-verifier --features field-inline,akita, and--features prover-fixtures,field-inline,akita --test-threads 1-p jolt-verifier --features prover-fixtures --test-threads 1(140 tests)Clippy passes for
jolt-transcript(all features, andbn254,solinas,binary,prover-grinding),jolt-verifierandjolt-proverwithakita,prover-fixtures,jolt-verifierwithfs-audit,akita,prover-fixtures, and CI's field-inline crate set with-D warnings.cargo fmt,taplo fmtandcheck_style_invariants.pypass.Final round at this head (
ed90d31d7, Akita pine9d85d1f7), all passing:cargo nextest run --cargo-profile ci -p jolt-akita(95 tests)-p jolt-verifier --test fs_obligations, withfs-auditand withfs-audit,akita--cargo-profile ci -p jolt-verifier --test fs_attacks --features akita,fs-audit,prover-fixturescargo clippy -p jolt-verifier --all-targets --features akita,prover-fixtures, andcargo clippy -p jolt-akita --all-targets -- -D warningsThe runs above the final round were on the previous commit, which differs only in the Akita pin (
062106ee3, before the merge of #253's head) and the re-blessed inventories.Follow-ups
field_sampling_is_certifiedsetup check fromtranscript_binding.rs. That is Optimize read-write memory commitments #253's change, not this PR's.🤖 Generated with Claude Code