Repository navigation
Conversation
Transcript atoms need one fixed-width canonical codec that every message type implements, including extension fields, which had an encoding but no checked decoder. Move `from_bytes_le_checked` from `CanonicalEncoding` into a new `CanonicalDecode: CanonicalBytes` supertrait and implement it for the prime fields, `FpExt2`/`FpExt4`/`FpExt8` (coefficient-wise, rejecting any non-canonical coefficient), unsigned integers, and byte arrays. `FpExt8` gains the `CanonicalBytes` encoding its siblings already had.
A proof is its argument string. `ProverTranscript` appends each prover message and absorbs exactly the appended bytes; `VerifierTranscript` reads messages back through `receive*`, absorbs exactly the bytes read, poisons itself on the first failure, and rejects trailing bytes in `finish`. Code both roles run identically targets the `Channel` trait, whose `exchange` sends for the prover and receives in place for the verifier, so sub-protocols compose by sharing one transcript. Atoms are the `CanonicalBytes`/`CanonicalDecode` codecs; shapes are positional. `challenge` is exactly uniform through `Field::random`, and `challenge_small` decodes 16 squeezed bytes through the field's small challenge set. The sponge is a type parameter whose `Sponge::ID` is bound into the 64-byte `ProtocolId`. `Preview` exposes a detached sponge copy for proof-of-work search, and `grind`/`check_grind` implement low-bit-first proof of work over canonically encoded nonces. Under `logging`, each transcript records site-tagged events with argument-string ranges. The unused spongefish split traits and `OptimizedChallenge` are removed; the legacy symmetric facade stays until its consumers migrate.
G1 and G2 encode as their compressed points and GT as its compressed `Fq12` element through `CanonicalBytes`/`CanonicalDecode`, and serde now reuses that codec instead of a parallel one. Decoding keeps every existing check: exact length, on-curve and subgroup membership for points, and nonzero, unitary, r-torsion membership for GT. `JoltGroup` and vector commitment outputs require the codec instead of `AppendToTranscript`, `NoCommitment` decodes from the empty string, and jolt-crypto no longer depends on jolt-transcript.
`challenges_small` draws a vector from the small challenge set and `challenge_powers` expands one exactly uniform challenge into its powers, so the many call sites that need either share one definition.
Callers name `Blake2b512` and `Keccak` through jolt-transcript, so no protocol crate depends on spongefish directly.
Opening proofs now live in the caller's transcript: `open` and the batch provers write their prover messages into a `ProverTranscript`, and `verify` and the batch verifiers read them back from a `VerifierTranscript`, so `CommitmentScheme::Proof`, `BatchOpeningScheme::Proof`, and `ZkBatchOpening::proof` are gone. Each scheme owns its commitments' wire codec through `send_commitment`, `receive_commitment`, and `absorb_commitment`, because commitment sizes are scheme-specific. Batch statements, prefix-packed claims, and hiding commitments absorb as positional public atoms without labels, and RLC coefficients come from `challenge_powers`. Transcript failures surface as `OpeningsError::Transcript`. jolt-poly's `transcript` feature, whose only user was this crate's `EvaluationClaim` absorb, is removed. The Dory-backed tests compile with the next commit, which ports jolt-dory.
Every value dory-pcs absorbs through `append_serde` is now a prover message of the caller's transcript, so the argument string holds exactly Dory's messages in Fiat-Shamir order and the separate `DoryProof` wire type, its serde caps, and the round-count pre-parser are gone. dory-pcs verifies from a proof struct handed over up front, so the verifier rebuilds that struct by reading the messages on a scratch copy of the transcript, then runs `dory::verify` with an adapter that receives each message from the live transcript and requires it to equal the value dory-pcs absorbs. The Σ₁ responses, which dory-pcs never absorbs, follow Dory's messages on both sides, so no proof byte is read without being absorbed. `DoryCommitment` encodes as its GT atom. A byte-flip sweep over transparent and ZK openings replaces the struct-mutation tamper tests. Tests needing the jolt-akita dev-dependency compile once jolt-akita is ported.
…ranscript There is no sumcheck proof type anymore. Recorders write round messages into a `ProverTranscript` and `SumcheckVerifier` reads them back from a `VerifierTranscript`: clear rounds as compressed polynomials, uni-skip rounds in full, committed rounds as vector commitments. Every round travels at its public degree bound (the prover pads instead of trimming), so the verifier derives each message's width rather than trusting a prover-chosen length, and committed rounds commit `degree + 1` coefficients, fixing BlindFold's layout publicly. Output-claim commitment counts become a public input to `verify_committed`. Clear input claims absorb as public values, clear output claims are sent, and round challenges come from the small challenge set as before. `SumcheckProof`, `ClearProof`, the labeled round wrappers, the transcript label constants, `CommittedRound`, `CommittedRoundWitness`, and the proof-encoding and round-count errors are removed. The crate's tests are ported in the following commit.
`BlindFoldProof` is gone: `prove` writes every BlindFold message into the caller's `ProverTranscript` and `BlindFoldProtocol::verify` reads them back from a `VerifierTranscript`, at counts fixed by `BlindFoldDimensions` and the final-opening layout rather than by prover-supplied vector lengths (row openings are read at the protocol's row width). The folding commitments are sent as one block before the folding challenge; the relaxed instances are no longer re-absorbed, because every commitment in them is already bound by that block or by the stage that produced it. The folded evaluation outputs and blindings, previously checked but never absorbed, are now sent before the evaluation openings, and both sides send those openings in one per-coordinate order. The outer and inner Spartan sumchecks use the shared compressed round codec. Checks that consumed proof values before their absorb position now run right after the corresponding receive. The crate's tests are ported separately.
Round trips now prove into a `ProverTranscript`, verify from the argument string, and require the verifier to finish with no bytes left. Struct mutation tamper tests become argument-string attacks: every byte flip must fail exactly its round's check (or decode), every truncation and any trailing data must reject, non-canonical coefficients reject, and swapped, replayed, or re-sessioned rounds reject. A wire pin fixes the full and compressed round layouts, including zero padding to the degree bound. Tests of states the fixed-width wire format can no longer express (wrong proof encoding, round-count and degree mismatches, short compressed rounds, labels) are removed. Extension-field round trips are removed because the transcript paths draw round challenges through `CanonicalEncoding::from_challenge_bytes`, which the extension types do not implement; no production caller runs jolt-sumcheck over an extension field. The external-consumer fixture runs over Keccak.
End-to-end tests now write each committed stage and then the BlindFold proof into one argument string, read the stages back with `verify_committed`, verify BlindFold from the same transcript, and require it to finish with no bytes left. The per-field tamper tests are replaced by a sampled byte-flip sweep across the whole argument string plus truncation, trailing-data, and wrong-session rejections. Unit fixtures build consistencies and output-claim commitments directly, and verifier unit tests mutate typed messages written in the wire order. Tests of length mismatches the verifier no longer reads from the proof are removed. The empirical randomness test now samples the production prover.
A Jolt proof is now `JoltProof { protocol, narg }`: the protocol config
plus the argument string. The verifier reads every prover message from
`narg` in protocol order and finishes with an EOF check, so no proof
field exists outside the transcript.
- The proof header (trace length, RAM K, rw/one-hot configs, trace
order, untrusted-advice flag) is the first message; the commitments
follow at counts fixed by the RA layout.
- The public preamble absorbs the verifier's own protocol config.
- Stage output claims are received by shape: each member's wire
openings in canonical order, with aliased openings filled from their
sources. Stage 6b's runtime bytecode-RA dedup goes through the same
helpers, and its ZK commitment count is derived from the verified
rounds (`verify_zk_with`).
- Stage 4's staged advice/program-image openings are sent before the
batch in clear mode, because the input claim consumes them.
- Stage 8 takes the received commitments and runs the transcript
sequence of the shared homomorphic batch opening.
- The scalar-challenge endianness axis is gone; challenge semantics are
fixed by the transcript.
The verifier test modules are ported in a later commit.
The prover runs every stage on one `ProverTranscript` and returns its argument string as the proof, so it no longer assembles stage proofs, claims, or a joint opening proof. - Stage 0 mirrors the verifier's seeding: it validates and sends the proof header, absorbs the public preamble, sends the commitments, and absorbs the public ones. - The stage driver returns the recorder's witness. Clear recorders send the round polynomials and output claims; committed recorders retain their openings for BlindFold. - In clear mode, stage 4 sends the staged advice/program-image openings after the gamma draws and only the post-round openings after the rounds. In ZK mode it commits the full canonical order. - Stage 8 writes the homomorphic batch opening to the transcript. - BlindFold replays the argument string through the verifier's `verify_stages`. It checks that the replay consumed every byte and reached the forward sponge state, then proves onto the same transcript. The packed (Akita) path is ported with jolt-akita.
…ent string The twin tests now hand the prover's argument string to the verifier, which must receive the proved claims, consume every byte, and reach the prover's sponge state. The tampered-BlindFold test flips a byte of the trailing BlindFold openings.
`ProtocolSponge` replaces `ProtocolTranscript`, `ProofTranscript`, and `VerifierTranscript`. The `transcript-*` features now choose the sponge (Blake2b512 by default, Keccak, or Poseidon) passed to the prover and verifier. jolt-eval verifies with the SDK's sponge, and its transcript symmetry invariant drives the NARG prover/verifier pair.
Messages use jolt-field's codec traits instead of an `Atom` trait. The spec now gives the actual API and framing, the proof header, Dory's scout-and-check integration, the BlindFold replay through `verify_stages`, the stage-level wire decisions, the SDK sponge selection, and the API gaps found while porting Akita.
…ifier The stage round trips no longer hand-copy each verifier stage body. A single `verify_through` runs the production stage verifiers over the prover's argument string, then requires EOF and the prover's sponge state. - The ZK stage tests now also verify (stages 4 and 6b previously had no replay). - The field-inline e2e tamper tests overwrite the FieldRdInc commitment bytes with another well-formed commitment and require rejection. - The ZK BlindFold-tamper case is dropped: on an argument string it is zk_e2e's trailing-byte flip.
… fuzzers A fixed script over every message, challenge, and grinding kind pins the argument string and challenge bytes for Blake2b512, Keccak, and Poseidon. The bench and the determinism and framing-injectivity fuzzers run on the NARG transcript.
Akita runs on the caller's jolt-transcript, so the Jolt packed path hands its live transcript to Akita's `batched_prove`/`batched_verify`. Akita's messages land in Jolt's argument string, and the only `finish` is Jolt's. The bridge that squeezed a Jolt challenge to seed a separate Akita session is deleted, along with `AkitaBatchProof`. - Before Akita's own messages, Jolt absorbs the setup's schedule catalog digest and sends the selected schedule row. Akita's root absorbs its descriptor, the group commitments, points, and evaluations itself. - Akita commitments travel as a 65-byte header plus exactly `coeff_len * 16` payload bytes, decoded with checked deserialization. The hiding commitment is a fixed 16-byte evaluation. - The prover's packed stage 0 mirrors the verifier's seeding: header, public preamble, commitments, then the public commitments. - `AkitaScheduleLookupKey` follows Akita's rename to `ScheduleLookupKey`. - The root `[patch]` redirects jolt-transcript to the workspace member, so Akita and Jolt share one transcript crate. A verifier setup that differs only in `default_layout_digest` now accepts a direct single-group opening: that digest is not an input of such an opening.
`jolt_verifier::sites` names the protocol regions: the preamble, the commitments, each stage, and BlindFold. The prover and verifier set the same site on entry to each region, so under the new `logging` feature every transcript event is attributed to its region. Sites never reach the sponge. This retires the `fs-audit` thread-local scopes: the `fs_scope` attribute, the `fs_scope_guard!` macro, and `fs_audit` are deleted.
The attacker now controls the argument-string bytes directly. - Sumcheck: the targets feed fuzzer bytes as compressed or full rounds, corrupt honest proofs, and differentially check compressed verification. Each run ends with an EOF check. - Dory: the targets mutate honest transparent and ZK openings byte-wise (flip, truncate, append, negate a group element, swap or delete an element-width range) and require rejection. They also check that neither mode's verifier accepts the other mode's opening. - Seeds are regenerated for the new encodings.
The stock prover and verifier run on Keccak by type argument alone, and the sponge identity separates proofs: a Keccak proof does not verify under Blake2b.
…sites The Fiat-Shamir source census follows the NARG API. It inventories public absorbs, prover messages, challenges, transcript delegations, site tags, and the schema of the proof's message and public-statement types, so moving any transcript operation needs a reviewed inventory update. It scans source only and runs without a feature; regenerate with JOLT_FS_BLESS=1.
Draw tests compare the production draw against the documented draw sequence on identical transcripts (values and sponge fingerprint) instead of recording doubles. Order tests assert the production order functions directly. Tests of removed validators, proof-consistency checks, and the stage-4 domain separator are deleted.
…argument strings The fixture cache stores NARG proofs (magic JVCF0006). The ZK completeness test pins the BlindFold protocol shape replayed from the argument string. The tamper fuzzers mutate honest argument strings and public statements, the deserialize and input-validation fuzzers decode arbitrary bytes as proofs and headers, and the fuzz fixtures are regenerated. The tamper, ZK-audit, and FS support modules are disabled until their suites are ported.
The live Fiat-Shamir attacks run the stock verifier on an `AuditSponge` that records the honest squeeze stream and replays it frozen. Each attack (a preprocessing-digest flip, a stage-1 sumcheck equivocation, a cancelling final-opening commitment pair) must verify under frozen challenges and reject under live ones. The attacks edit argument-string bytes, which they locate through the `logging` event log. CI and the testing guide use `logging` in place of `fs-audit`.
…tion deadline The typed tamper manifest is replaced by the transcript event log. - **Sweep:** for every recorded prover message (every message, or a capped deterministic sample for the larger fixtures) a byte flip must be rejected, and the rejection must land by the message's deadline region: - clear builds: the region of the next challenge; - ZK builds: stage 8 or BlindFold. - **Coverage:** message ranges must tile the argument string. - **Structural tampers:** truncation, trailing bytes, an empty proof, and a protocol mismatch reject with their typed errors. - **Statement, header, commitment-substitution, and committed-program tampers** carry over the old typed suites' intent. - **ZK independence:** two ZK proofs of one statement differ in every hiding message, and their message bytes pass uniformity checks. The ignored placeholder tests are deleted. CI runs the verifier suites with `logging`.
`Transcript`, `AppendToTranscript`, the label helpers, `DigestTranscript`, `SpongeTranscript`, the `Blake2bTranscript`/`KeccakTranscript`/ `PoseidonTranscript`/`LegacyBlake2bTranscript` aliases, and the spongefish setup helpers have no consumers left. The crate now exposes only the NARG channel. The `transcript-*` features now just select which sponges are compiled in, and the blake2, digest, and rand dependencies are dropped. The legacy test suites go with it; the NARG and known-answer suites remain.
…permutation PoseidonSponge emitted the 32 little-endian bytes of a field element, whose top byte is at most 0x30. A challenge drawn at a squeeze offset that is not 32-byte aligned then saw biased bytes, costing about 2.4 bits per challenge, while the transcript promised exact challenges. Each permutation now yields its low 16 bytes, within 2^-125 of uniform. Poseidon proofs change; the Poseidon known answers are regenerated.
…eads - receive_bytes compares every block it returns with the block spongefish read and absorbed, so a cursor drift (an atom decoding other than its NUM_BYTES) fails as CursorMismatch instead of handing out unbound bytes, including to Dory's parse-ahead. - receive_nonce range-checks with checked_shr, and a proof that ends inside a nonce reports Truncated. - SMALL_CHALLENGE_BYTES, PROTOCOL_ID_LEN, and GRINDING_SEED_LEN are crate-private: nothing outside the crate uses them. - A test pins that an exact challenge rejects an out-of-range candidate and resamples from the next squeeze. - Nonce docs name the LEB128 Nonce message, not a u32.
The prover sent the six u8 widths as six 1-byte messages and the verifier read one 6-byte message. Byte sponges absorb those identically, but a length-binding sponge such as Poseidon does not, so every honest Poseidon proof was rejected. Both sides now exchange one [u8; 6]. CommittedOutputClaimShape documents and debug-checks its nonzero row length.
arkworks decodes any compressed G1/G2 bytes with the infinity flag as the identity whatever their x bits, so an identity commitment had many accepted encodings: byte-level proof malleability, though spongefish absorbs the re-encoding and the challenges do not move. Decoding now requires the input to equal the point's canonical encoding.
The folded evaluation outputs and blindings, and the outer sumcheck's Az/Bz/Cz with the error-row opening, were sent in prove.rs, read in verify.rs, and restated by the test that builds argument strings. They are now FoldedEvaluations and OuterClaims with send and receive, used by all three. The Fiat-Shamir inventory is re-blessed for the moved sites and the single header message.
…ponge The audit sponge taped every Blake2b it built, so a seeded fork attached to the session tape at offset zero, and forks built on Akita's worker threads panicked outside the session: the Akita fs_attacks leg failed. A fork is a function of a taped seed and a proof counter, so freezing the transcript already freezes it; a sponge whose first absorb is the fork tag now runs plain Blake2b on any thread. Transcripts are not Clone, so the clone and scout bookkeeping goes too.
jolt-verifier's JoltSponge and the SDK's ProtocolSponge each picked the sponge. JoltSponge now selects Poseidon, then Keccak, then Blake2b by jolt-verifier feature, with an explicit precedence, and builds Blake2b unconditionally so every feature set has a default; ProtocolSponge is that alias and the SDK's transcript features forward to jolt-verifier.
The check covered only the root workspace; the fuzz harnesses and the external sumcheck consumer resolve their own graphs. It now checks every committed workspace whose lockfile contains spongefish and reports a failed cargo tree instead of hiding it.
- The spongefish-state spec is implemented. It describes the LEB128 nonce, typed TranscriptError reporting, the empty instance every transcript passes, block-chunked byte messages, and Poseidon's 16-byte squeezes, and moves transcript-bound prover randomness, which nothing implements, from the invariants to the non-goals. The narg spec is marked amended where it still specifies previews. - WARNINGs on the RAM output-check and booleanity draws name the draws the code makes; comments no longer cite challenge_scalar. - The Dory parse-ahead carries a WARNING that it restates dory-pcs's message order, and verify_zk's ordering contract names stage 4's staged openings as the exception and why they stay sound. - CLAUDE.md and the sumcheck README describe the sponge-parameterized transcript API.
📏 PR diffNote Actual code changed: 15,368 lines🟢 +6,118 added 🔴 −9,250 removed Tests, docs, fixtures, and helper scripts excluded.
Every changed line is classified once. |
Pins every Akita git dependency to LayerZero-Labs/akita 7109b3ea, the Akita head that runs on this branch's transcript and pins Jolt 9d2eb0f, and regenerates the lockfile. The root patch keeps Akita's jolt-* crates on this workspace.
Benchmark comparison (crates)Same-runner A/B: 2 of 63 benchmarks differ by more than ±10%:
All 63 benchmarks
24 benchmark(s) present in only one run: |
Akita 10a13def drops akita-types' unused dependencies and gives wasm a getrandom backend; the code Jolt builds is unchanged.
read_proof parses the Dory proof from the unread bytes before the live transcript receives them, and reported running out of bytes as a failed verification. The message sweeps require every cut to be rejected as truncated, as the live transcript reports on the same bytes; a malformed element still fails verification.
…endencies - The fuzzing-only evaluate_mapped_expression names JoltOpeningId, which only that cfg imported nowhere. - The WASM verifier template names the SDK's ProtocolSponge instead of the removed VerifierTranscript. - jolt-verifier (ark-serialize), jolt-sumcheck (bincode), jolt-blindfold (serde), and jolt-sdk (jolt-transcript) no longer use those dependencies since proofs became argument strings (cargo machete); the SDK's transcript features forward to jolt-verifier alone.
Lines this branch adds now follow the machine-checked import rule (scripts/check_style_invariants.py): types, traits, and constants are imported and named by their short name, with imports gated on the same features as their uses, and fmt::Result aliased where it would shadow Result. The external-consumer and SDK manifests are taplo-formatted.
The checked-in bundles hold proofs from before this branch's transcript changes, so the fuzz replay rejected them as honest fixtures. Regenerated with the documented generate_fuzz_fixture runs, which verify each bundle through the fuzz target's own decoding.
…ng sponges spongefish's Keccak duplex and the byte-level Poseidon sponge squeeze by reading their rate without permuting, so an absorb after squeezing one byte or two leaves the same state. Exact challenges rejection-sample a variable number of bytes, so on those sponges distinct retry histories could reconverge at the next absorb. Sponge gains ABSORB_FORGETS_SQUEEZE_WIDTH, and on such sponges every exact challenge absorbs the width it squeezed as a public u64. Blake2b retains the width and is unchanged. A test pins each sponge's flag to its real absorb-after-squeeze behavior; the Keccak and Poseidon known answers are refrozen. The spec documents the binding and no longer names downstream consumers outside this repository.
Channel::record_fork_draw logs a ForkAbsorb and a ForkSqueeze event at the current site without touching the sponge. A prover records only the accepted counter's draws after its search, in the order the verifier redraws them, so off-transcript fork draws show up in both roles' event logs and the logs still compare.
Exact challenges draw u64 words, which the squeeze helper split into single-byte squeezes. A word-sized arm between the 32-byte blocks and the bytes cuts those calls; every sponge's output stream is independent of how squeezes are split, so proofs are unchanged and the known answers still hold. The spec records the verifier cost of squeeze-then-grind.
Picks up the exact-challenge squeeze-width binding on Keccak and Poseidon, Channel::record_fork_draw, and word-sized squeezes.
Ports main onto the argument-string transcript: the native Akita trace batching (#2012), backends bound to execution contexts (#2028), unified trace rows (#1996), whole-bytecode commitments (bytecode chunking removal), the binary fields and their sum-check (#2034 and the binary backend), and the clear proof-type cleanup, which this branch already subsumes. Binary fields gain the spongefish codecs every transcript atom carries, and main's new tests are rewritten for argument strings. Akita's runtime witness-chunking policy (LayerZero-Labs/akita#246, now in akita#185) moves chunking out of the commitment config, so each Jolt catalog family fixes its own through `CatalogChunking`, and the schedule generator preplans the direct rows below the setup-offloading cutover.
LayerZero-Labs/akita#185 at a4978d59 carries Akita main, including its runtime witness-chunking policy, and pins this branch's merge commit.
…trip Checked decoding moved to CanonicalDecode on this branch; main's field-inline limb test called it through CanonicalEncoding.
|
Thanks, Markos. This PR is now superseded by the transcript stack below, which moves Jolt onto spongefish 0.8.0's
One design difference: grinding searches nonces on a copy of the prover's sponge, so the prover alone needs spongefish's Since this branch conflicts with |
Summary
Jolt proofs become NARG argument strings.
jolt-transcriptis a typed layer over spongefish'sProverStateandVerifierState. Proof messages are declared by typed claims structs per stage, not by hand-written per-stage wire aggregates. Akita opens inside the Jolt transcript, so the bridge and the separate Akita proof blob are gone.The design and its invariants are in
specs/jolt-transcript-spongefish-state.md, which amendsspecs/jolt-transcript-narg.md. The typed messages are specified inspecs/jolt-verifier-typed-messages.md. Main points:yolocrypto;scripts/check-no-yolocrypto.shchecks the root workspace and every standalone workspace in CI. Grinding is squeeze-then-grind on seededForks, with a canonical LEB128Nonce.Clone. The BlindFold replay compares a 32-byte checkpoint squeezed after the stage spine on both sides.receive_bytesreturns is the byte the sponge absorbed.The branch is merged with main (not rebased, so the commits Akita pins stay reachable). The merges port main onto the NARG transcript: the Spartan and HyperKZG crates (#1880), native Akita trace batching (#2012), backends bound to execution contexts (#2028), unified trace rows (#1996), whole-bytecode commitments in place of bytecode chunks, and the binary fields with their sum-check (#2034), which gain the same spongefish codecs as every other atom. Main's Akita chunk-profile binding (#1976) is kept. Main's clear proof-type cleanup is already covered by this branch's recorders.
Akita's runtime witness-chunking policy (LayerZero-Labs/akita#246, merged into akita#185) moves chunking out of the commitment config. Each Jolt catalog family fixes its own chunking through
CatalogChunking, andgen_jolt_schedulespreplans the direct rows below the setup-offloading cutover. The checked-in catalogs are unchanged.Companion PR: LayerZero-Labs/akita#185, which pins this branch.
Since the last review round this branch also:
Sponge::ABSORB_FORGETS_SQUEEZE_WIDTH);Channel::record_fork_draw, which logs accepted fork draws in the event log only;The Keccak and Poseidon known answers are refrozen.
Changes
jolt-transcript: the NARG prover/verifier channel, sites, forks, grinding, and nonce. The legacy symmetric transcript,Duplex, andPrevieware deleted.jolt-field,jolt-crypto,jolt-dory: atoms are spongefish codecs. G1/G2 decoding accepts only canonical encodings, including for the identity.ProtocolSpongeisjolt_verifier::JoltSponge, the one place the sponge is chosen.Testing
jolt-transcript,jolt-field(withbinary),jolt-sumcheck(committed),jolt-claims,jolt-dory,jolt-blindfold,jolt-akita(91/91, and 98/98 withfield-inline),jolt-kernels,tracer, and the binary-field sum-check suite ported to argument strings (19/19).jolt-verifiersuites withprover-fixtures,logging93/93,+zk88/88,+field-inline99/99,akita,prover-fixtures,logging79/79,prover-fixtures,field-inline,akita,logging81/81 (these include the Fiat-Shamir attack legs and the message and truncation sweeps). The FS inventories are re-blessed for the whole-bytecode commitment and the Akita stage-8 changes.jolt-proversuites:prover-fixtures30/30,+zk32/32,+field-inline41/41,+field-inline,zk26/26,akita,prover-fixtures41/41,prover-fixtures,field-inline,akita37/37. The verifier fuzz fixture bundles are regenerated.cargo clippylane inrust.ymlwith-D warnings;cargo fmt,taplo,cargo machete,check_style_invariants.py,check-no-yolocrypto.sh,check-shared-field-identity.sh; thejolt-verifierfuzz harness builds.z3-verifier(no native Z3 on this machine) and the WASM verifier end to end; CI runs both.Security Considerations
This changes the Fiat-Shamir transcript for every proof, so it affects soundness directly. Review focus:
receive*, which absorbs it. Dory parses ahead fromunread(), but every parsed byte is received and compared afterwards;read_proofrestates dory-pcs's message order and carries a WARNING to that effect.jolt-transcript/fork/v1, seed,LE32(counter)).ProofHeaderfields travel as byte arrays, as one[u8; 6]message for the six widths, because spongefish decodes onlyu32and byte arrays.Not addressed here: prover blindings come from the OS RNG, not spongefish's transcript-bound
ProverState::rng()(now a stated non-goal of the spec).Breaking Changes
jolt-transcriptAPI: the NARG prover/verifier channel replaces the symmetric transcript.jolt-verifier'stranscript-*features (Poseidon, then Keccak, then Blake2b).a4978d59(akita#185), which pins this branch's merge commitf15e9b17. Merge order: merge this PR, repin Akita to Jolt main and merge it, then repin this repo to Akita main.