Skip to content

feat: Akita lattice PCS integration — jolt-prover-legacy packed prover#1676

Open
markosg04 wants to merge 23 commits into
mainfrom
feat/jolt-legacy-prover-akita-integration
Open

feat: Akita lattice PCS integration — jolt-prover-legacy packed prover#1676
markosg04 wants to merge 23 commits into
mainfrom
feat/jolt-legacy-prover-akita-integration

Conversation

@markosg04

@markosg04 markosg04 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Prover half of the Akita integration, on top of #1675 — the diff shows both
halves until #1675 merges; review only the jolt-prover-legacy commit here.

What

  • zkvm/packed.rs: the packed prove pipeline — OneHotTrace one-hot
    witness assembly and owned group commit, the fused increment claim path
    (four fused-inc consumer val stages inside the nine-stage bytecode
    read-raf, then the stage-7 lattice hamming mega-reduction), advice byte
    objects, the precommitted program one-hot, and a stage 8 that opens
    OneHotTrace natively at its common point from the commit hint (no
    retained witness) with the auxiliary objects going through the joint
    packed opening.
  • zkvm/packed_witness.rs + claim reductions: witness assemblers (every
    column, including the MSB, is a full K×T one-hot grid) and the
    reconstruction phase producing per-column leaf claims.
  • transcripts/verifier_native.rs: legacy-trait facade over
    jolt-transcript so the prover drives the verifier-native FS stream.
  • field/akita.rs: Fp128 JoltField implementation.
  • Features: akita wires the backend deps and follows the shared
    K=16/K=256 trace-length toggle (log_T = 25).

Notes

  • e2e coverage: muldiv / advice / committed-program / forced-K256 over
    Akita, with live claim-wire tampers (fused-inc reduced claim, chunk and
    MSB finals) and per-object tamper checks; the verifier-side
    fixture-driven tamper suite (see feat: Akita lattice PCS integration — verifier path and shared semantics #1675) generates its fixtures through
    this prover.
  • Perf at 2^20 sha2-chain, same machine back-to-back: prove ~7.8-8.0 s vs
    dory 13.64 s (~1.7× faster)
    ; verify 35 ms vs 610 ms. Setup is 0.16 s
    (production envelopes sized from the checked-in schedule catalogs — the
    former ~83 s planner/family-init cost is gone); verifier preprocessing
    ~0.6 s, timed separately by the harness.
  • The recent commits: a prover perf pass (lazy one-hot chunk columns via
    RaPolynomial, shared parallel pushforwards, Gruen split-eq fused
    reduction — 2^24 packed prove 156.6 s → 150.3 s, ~4 GiB less peak
    memory); the read-raf dissolution of the standalone IncVirtualization
    phase and the stage-6b fused-inc member (details in feat: Akita lattice PCS integration — verifier path and shared semantics #1675; proof drops a
    full log_T-round stage, measured 1114 → 1066 sumcheck field elements at
    2^24); and the perf(akita): size setup from Jolt schedules #1685 follow-ups re-expressed over both (the
    OneHotTrace naming, streamlined owned commits opening from the hint,
    and catalog-driven setup sizing — authored by @quangvdao).

Testing

  • CI: clippy lanes for prover host,akita; the test-prover-akita job
    runs the full suite under akita plus the verifier fixture suite.
  • Local: full prover suite green under host, host,zk, and host,akita;
    all four akita e2es; base muldiv both modes.

Protocol design by Quang Dao (quangvdao, #1683), re-expressed per the
distilled commit series on the integration branch.

@github-actions github-actions Bot added spec Tracking issue for a feature spec implementation PR contains implementation of a spec labels Jul 15, 2026
@markosg04
markosg04 force-pushed the feat/jolt-legacy-prover-akita-integration branch from e8e90db to 5d9b4c8 Compare July 15, 2026 17:22
@markosg04
markosg04 force-pushed the feat/jolt-verifier-akita-integration branch from 9394697 to 4951e28 Compare July 15, 2026 17:31
@markosg04
markosg04 force-pushed the feat/jolt-legacy-prover-akita-integration branch from 5d9b4c8 to b4c6cd9 Compare July 15, 2026 17:32
@markosg04
markosg04 marked this pull request as draft July 15, 2026 17:33
@markosg04
markosg04 changed the base branch from feat/jolt-verifier-akita-integration to main July 15, 2026 17:42
@markosg04
markosg04 marked this pull request as ready for review July 15, 2026 18:00
@markosg04
markosg04 force-pushed the feat/jolt-legacy-prover-akita-integration branch from b4c6cd9 to ee4de90 Compare July 17, 2026 06:27
@socket-security

socket-security Bot commented Jul 17, 2026

Copy link
Copy Markdown

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@markosg04
markosg04 force-pushed the feat/jolt-legacy-prover-akita-integration branch from ee4de90 to 5660be8 Compare July 17, 2026 06:40
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Benchmark comparison (crates)

group                           main_run                               pr_run
-----                           --------                               ------
EqPolynomial::evals/17          1.00  1914.0±50.52µs        ? ?/sec    1.08      2.1±0.02ms        ? ?/sec
EqPolynomial::evals/19          1.00      7.2±0.10ms        ? ?/sec    1.13      8.2±0.27ms        ? ?/sec
EqPolynomial::evals/20          1.00     15.3±0.09ms        ? ?/sec    1.09     16.6±0.12ms        ? ?/sec
EqPolynomial::evals/22          1.00     57.7±0.34ms        ? ?/sec    1.09     63.0±0.44ms        ? ?/sec
EqPolynomial::evaluations/14    1.00   524.2±12.28µs        ? ?/sec    1.18   620.8±33.76µs        ? ?/sec
EqPolynomial::evaluations/18    1.00      6.5±0.07ms        ? ?/sec    1.12      7.2±0.08ms        ? ?/sec
EqPolynomial::evaluations/20    1.00     28.9±0.36ms        ? ?/sec    1.07     31.0±1.54ms        ? ?/sec
Fr * Fr                         1.00     25.4±0.07ns        ? ?/sec    1.06     26.8±0.34ns        ? ?/sec
Fr::from_bytes                  1.00    157.0±0.82ns        ? ?/sec    1.05    165.3±0.71ns        ? ?/sec
Fr::mul_u128                    1.00     21.3±0.18ns        ? ?/sec    1.14     24.4±0.44ns        ? ?/sec
Fr::to_bytes                    1.00     17.5±0.07ns        ? ?/sec    1.15     20.0±0.12ns        ? ?/sec
Polynomial::bind/14             1.00    130.1±5.72µs        ? ?/sec    1.18    153.4±5.40µs        ? ?/sec
Polynomial::bind/18             1.00  1926.2±25.12µs        ? ?/sec    1.13      2.2±0.10ms        ? ?/sec
Polynomial::bind/20             1.00      7.7±0.20ms        ? ?/sec    1.11      8.5±0.28ms        ? ?/sec
Polynomial::evaluate/20         1.00     42.0±0.34ms        ? ?/sec    1.06     44.4±0.58ms        ? ?/sec
append_bytes/Blake2b/256B       1.00    323.6±5.78ns        ? ?/sec    1.06    344.5±6.84ns        ? ?/sec
append_bytes/Keccak/256B        1.00   823.8±14.54ns        ? ?/sec    1.11   917.9±22.34ns        ? ?/sec
challenge/Blake2b               1.00    642.5±6.17ns        ? ?/sec    1.15   740.9±57.02ns        ? ?/sec
challenge/Keccak                1.00    396.1±5.25ns        ? ?/sec    1.11    440.2±6.92ns        ? ?/sec
challenge/Poseidon              1.00     46.2±0.86µs        ? ?/sec    1.06     48.8±0.21µs        ? ?/sec
g1_add                          1.00    411.6±5.76ns        ? ?/sec    1.12    461.8±4.30ns        ? ?/sec
g1_deserialize_bincode          1.00      9.2±0.12µs        ? ?/sec    1.05      9.7±0.09µs        ? ?/sec
g1_double                       1.00    213.6±1.46ns        ? ?/sec    1.10    234.6±2.64ns        ? ?/sec
g1_msm/1024                     1.00     12.7±0.03ms        ? ?/sec    1.10     14.0±0.06ms        ? ?/sec
g1_msm/16                       1.00    534.8±4.51µs        ? ?/sec    1.12   600.1±15.27µs        ? ?/sec
g1_msm/256                      1.00      4.3±0.04ms        ? ?/sec    1.10      4.7±0.01ms        ? ?/sec
g1_msm/4                        1.00    254.3±2.86µs        ? ?/sec    1.12    285.0±6.45µs        ? ?/sec
g1_scalar_mul                   1.00     64.9±1.39µs        ? ?/sec    1.11     72.2±1.66µs        ? ?/sec
g1_serialize_bincode            1.00    109.9±1.52ns        ? ?/sec    1.10    120.6±0.77ns        ? ?/sec
g2_msm/256                      1.00     15.5±0.07ms        ? ?/sec    1.13     17.5±0.04ms        ? ?/sec
g2_msm/4                        1.00    775.6±6.99µs        ? ?/sec    1.12    865.5±5.70µs        ? ?/sec
g2_msm/64                       1.00      5.6±0.01ms        ? ?/sec    1.13      6.3±0.01ms        ? ?/sec
g2_scalar_mul                   1.00    320.4±3.09µs        ? ?/sec    1.13    361.2±3.82µs        ? ?/sec
gt_scalar_mul                   1.16    897.0±6.40µs        ? ?/sec    1.00    775.9±2.78µs        ? ?/sec
multi_pairing/16                1.00      5.8±0.02ms        ? ?/sec    1.10      6.4±0.05ms        ? ?/sec
multi_pairing/2                 1.16  1246.9±10.05µs        ? ?/sec    1.00  1071.5±35.94µs        ? ?/sec
multi_pairing/4                 1.00   1839.8±8.91µs        ? ?/sec    1.07  1968.4±25.20µs        ? ?/sec
multi_pairing/8                 1.00      3.2±0.01ms        ? ?/sec    1.10      3.5±0.01ms        ? ?/sec
pairing                         1.15   924.4±17.42µs        ? ?/sec    1.00    801.8±4.36µs        ? ?/sec
pedersen_commit/1024            1.00     12.8±0.05ms        ? ?/sec    1.11     14.1±0.29ms        ? ?/sec
pedersen_commit/16              1.00    605.1±8.93µs        ? ?/sec    1.14   691.0±78.37µs        ? ?/sec
pedersen_commit/256             1.00      4.3±0.01ms        ? ?/sec    1.12      4.8±0.15ms        ? ?/sec
pedersen_commit/4               1.00    318.1±1.32µs        ? ?/sec    1.12    356.3±2.19µs        ? ?/sec

Brings the verifier-side Akita integration onto main as one commit:
the fused increment claim pipeline (IncVirtualization as its own stage,
the stage-6b fused-inc reduction, the stage-7 lattice hamming
mega-reduction with the decode identity), MSB as a uniform one-hot
member, the native W_jolt one-hot group opened directly at one common
point behind layout-digest/metadata validation, the shared K=16/K=256
trace-length toggle, jolt-owned schedule catalogs injected through
akita's catalog hook, and the joint packed opening for auxiliary
objects. Includes the claims-level akita feature with the cfg'd
NUM_BYTECODE_VAL_STAGES const and fixed-size stage arrays, the
consolidated derive verify_clear with String stage labels, and the
append_length_prefixed transcript absorber rename (review feedback,
moodlezoup). Protocol design of the fused pipeline and native opening
by Quang Dao (quangvdao).

akita deps pin markosg04/akita @ cb13b79f (upstream main through the
quantum SIS cutover + the K16 preset now upstream as
LayerZero-Labs/akita#308); re-pin to upstream on its merge.

Minimal jolt-prover-legacy compat touches (full prover lands in the
follow-up PR): the three jolt_verifier::verify call sites drop the
removed zk argument. The akita fixture-driven tamper suite rides the
prover PR (it generates fixtures through the akita prover), so the
akita,prover-fixtures feature combo activates there.

Gates: workspace clippy strict (base, akita, prover-fixtures,
jolt-prover/kernels); muldiv e2e host + host,zk with main's prover;
verifier suites 75 base / 77 akita; modular crates green; fmt; typos.
The prover half of the Akita integration, on top of the verifier PR:
the packed prove pipeline (W_jolt one-hot witness assembly and group
commit, IncVirtualization between stages 5 and 6a, the stage-6b
fused-inc reduction, the stage-7 lattice hamming mega-reduction, the
reconstruction phase, native W_jolt open_batch at the common point with
auxiliary objects through the joint packed opening), the verifier-native
transcript facade, the Fp128 JoltField implementation, and the akita
cargo feature following the shared K=16/K=256 trace-length toggle.

Restores the verifier's akita fixture-driven tamper suite (it generates
fixtures through this prover): the exhaustive typed claim-wire sweep,
byte-level commitment sweeps, and proof-shape tampers, with all akita
manifest entries Active; plus the akita,prover-fixtures feature forward
and CI lanes (prover host,akita clippy + the test-prover-akita job).

e2e coverage: muldiv / advice / committed-program / forced-K256 with
live claim-wire tampers. Perf at 2^20 sha2-chain, same machine
back-to-back: prove 8.19s vs dory 13.64s (1.67x faster); verify 36.8ms
vs 610ms; setup ~83s per shape (offline, cacheable — a quantum
SIS-table cost, tracked with the remaining prover levers).

Protocol design by Quang Dao (quangvdao, #1683).

Gates: full prover suites 443 host / 443 host,akita + muldiv host,zk;
akita e2es x4; verifier fixture suite 84; clippy strict across modes;
fmt; typos.
@markosg04
markosg04 force-pushed the feat/jolt-legacy-prover-akita-integration branch from 5660be8 to 863fe1a Compare July 17, 2026 06:54
moodlezoup and others added 5 commits July 17, 2026 13:50
…TAGES

Address review: replace the Vec-typed committed publics/inputs and the
([F; 5], bool) / ([F; 5], F) store side-channels with
[_; NUM_BYTECODE_VAL_STAGES] arrays (5 base, 6 akita). The lattice store
stage is now read_raf_row_values' sixth staged value, folded uniformly by
fold_stage_values; the stage-6b akita arm resolves StageValue(5) from the
staged array instead of a separate store fold.

Gates: clippy base/akita/zk+fixtures clean; jolt-claims+kernels+prover 191
tests; jolt-verifier 118 base (prover-fixtures) / 77 akita; muldiv e2e
host and host,zk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The standalone log_T-round IncVirtualization phase (between stages 5 and
6a) and the stage-6b FusedIncClaimReduction member both disappear: the
bytecode read-raf gains four fused-inc consumer val stages that discharge
the four reduced inc claims directly.

Substituting Store(j) = sum_k val_store(k) ra(k,j) (and its complement,
valid because bytecode ra is one-hot per cycle) into the fused-inc
identities FusedInc*Store = RamInc and FusedInc*(1-Store) = RdInc turns
each inc claim into a read-raf-shaped stage: a bytecode val column
(store/notstore), the producing relation's cycle point, and the shared RA
product, with one shared FusedInc cycle factor (cycle-phase degree d+1 ->
d+2). The four claims are all available before stage 6a, so the timing
pin that forced the standalone phase (its store-selector opening feeding
the read-raf input fold) is gone, and no store opening exists anywhere.

- jolt-claims: LatticeReadRafAddressPhase folds the inc claims at
  gamma^5..8 (pc/shift/entry shift to gamma^9..11); new lattice cycle
  symbolics carry the FusedInc opening; committed mode resolves the fused
  stages through the staged store val and 1 - store, so the staged-val
  wires are unchanged; READ_RAF_CYCLE_STAGES (5 base / 9 akita) sizes the
  cycle-point and cycle-eq arrays, distinct from the staged-val wire
  count; the stage-7 hamming decode leg re-points to
  FusedInc@BytecodeReadRaf (same 6b cycle point).
- jolt-verifier: the inc-virtualization stage, its proof field and claims
  block, and the fused 6b member are deleted; stage 6a wires the four inc
  claims from stages 2/4/5; stage 6b builds nine cycle points and
  resolves the fused stages from the staged store fold.

Proof shrinks by the standalone phase (log_T degree-3 round polys and two
claim cells) net of one extra coefficient per 6b round; one fewer
Fiat-Shamir stage. The prover half lands with the jolt-prover-legacy
integration (#1676).
…TAGES

Address review: replace the Vec-typed committed publics/inputs and the
([F; 5], bool) / ([F; 5], F) store side-channels with
[_; NUM_BYTECODE_VAL_STAGES] arrays (5 base, 6 akita). The lattice store
stage is now read_raf_row_values' sixth staged value, folded uniformly by
fold_stage_values; the stage-6b akita arm resolves StageValue(5) from the
staged array instead of a separate store fold.

Gates: clippy base/akita/zk+fixtures clean; jolt-claims+kernels+prover 191
tests; jolt-verifier 118 base (prover-fixtures) / 77 akita; muldiv e2e
host and host,zk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- LatticeBooleanityCycle chunk/msb columns bind lazily via RaPolynomial
  (index-form through rounds 0-2) instead of materializing 17 full T-sized
  coefficient vectors up front
- one_hot_pushforwards: shared split-eq scatter for the 6a chunk G tables
  and the stage-7 increment pushforwards (one eq product per cycle, chunked
  parallel with cache-resident accumulators; stage 7 was single-threaded
  over a fully materialized eq table)
- FusedIncClaimReduction: GruenSplitEqPolynomial + parallel fold-out-in
  (compute_message was a serial 2^(log_T-1) loop over a dense eq table)
- LatticeIncColumns carries Arc'd Option<u8> lanes end to end: no more
  double lane clones into 6a and no fused-column clone into 6b

At 2^24 sha2-chain this takes the packed prove 156.6s -> 150.3s (stage 6b
23.1s -> 14.4s) and drops ~4 GiB of peak memory.
The standalone log_T-round IncVirtualization phase (between stages 5 and
6a) and the stage-6b FusedIncClaimReduction member both disappear: the
bytecode read-raf gains four fused-inc consumer val stages that discharge
the four reduced inc claims directly.

Substituting Store(j) = sum_k val_store(k) ra(k,j) (and its complement,
valid because bytecode ra is one-hot per cycle) into the fused-inc
identities FusedInc*Store = RamInc and FusedInc*(1-Store) = RdInc turns
each inc claim into a read-raf-shaped stage: a bytecode val column
(store/notstore), the producing relation's cycle point, and the shared RA
product, with one shared FusedInc cycle factor (cycle-phase degree d+1 ->
d+2). The four claims are all available before stage 6a, so the timing
pin that forced the standalone phase (its store-selector opening feeding
the read-raf input fold) is gone, and no store opening exists anywhere.

- jolt-claims: LatticeReadRafAddressPhase folds the inc claims at
  gamma^5..8 (pc/shift/entry shift to gamma^9..11); new lattice cycle
  symbolics carry the FusedInc opening; committed mode resolves the fused
  stages through the staged store val and 1 - store, so the staged-val
  wires are unchanged; READ_RAF_CYCLE_STAGES (5 base / 9 akita) sizes the
  cycle-point and cycle-eq arrays, distinct from the staged-val wire
  count; the stage-7 hamming decode leg re-points to
  FusedInc@BytecodeReadRaf (same 6b cycle point).
- jolt-verifier: the inc-virtualization stage, its proof field and claims
  block, and the fused 6b member are deleted; stage 6a wires the four inc
  claims from stages 2/4/5; stage 6b builds nine cycle points and
  resolves the fused stages from the staged store fold; fixture tamper
  coverage moves to the read-raf fused opening.
- jolt-prover-legacy: LATTICE_N_STAGES = 9; the address-phase pushforwards
  weight the fused stages by the per-cycle delta; the cycle prover binds
  the fused stream alongside the RA chunks and opens it at the bound
  cycle point; the committed bytecode reduction still folds six staged
  vals.

Proof shrinks by the standalone phase (log_T degree-3 round polys and two
claim cells) net of one extra coefficient per 6b round; one fewer
Fiat-Shamir stage. Measured at 2^24 sha2-chain: sumcheck payload 1114 ->
1066 field elements; prove time unchanged within noise.
moodlezoup and others added 7 commits July 20, 2026 10:16
specs/lattice-claims.md still described the superseded protocol (the
standalone IncVirtualization phase and the stage-6b FusedIncClaimReduction
member). Rewritten per the review follow-up:

- scope, module inventory, and typed ids drop the two deleted relations;
  relations/read_raf.rs joins the module tree
- the inc one-hotting chain's first section now specifies the four fused-inc
  consumer val stages at gamma^5..8 with the Store substitution, and the
  second (point-alignment) section is gone — the FusedInc opening is born at
  the shared 6b cycle point
- the store-binding section states the structural binding (no selector
  opening; the raw Store column stays the sixth staged-value wire)
- READ_RAF_CYCLE_STAGES = 9 vs NUM_BYTECODE_VAL_STAGES = 6 made explicit
- stage-7 inputs and the verifier/prover schedule re-pointed
Verifier-side half of the #1685 follow-ups, re-expressed over the read-raf
inc dissolution and the staged-value array refactor:

- rename: the native committed group W_jolt becomes OneHotTrace and its
  members become columns (one_hot_trace_columns, OneHotTraceLayout/Plan/
  SetupShape, ONE_HOT_TRACE_LAYOUT, OneHotTraceCommitmentMetadata/
  SetupMetadata); prose sweeps in the lattice specs
- streamlined commitments: the scheme gains commit_one_hot_group_owned and
  open_one_hot_group_from_hint so the prover commits owned columns and
  opens from the hint without retaining the witness
- catalog setup sizing: production OneHotTrace setup envelopes computed
  from the checked-in K=16/K=256 schedule catalogs (every catalogued
  sub-shape plus exact root A/B/D footprints, with overflow and
  scalar-layout guards), replacing planner-DP scans; upstream planner
  fallback retained for non-production shapes; only the fp128 D64 full and
  one-hot schedule families are enabled

The packed-prover half lands with #1676.
specs/lattice-claims.md still described the superseded protocol (the
standalone IncVirtualization phase and the stage-6b FusedIncClaimReduction
member). Rewritten per the review follow-up:

- scope, module inventory, and typed ids drop the two deleted relations;
  relations/read_raf.rs joins the module tree
- the inc one-hotting chain's first section now specifies the four fused-inc
  consumer val stages at gamma^5..8 with the Store substitution, and the
  second (point-alignment) section is gone — the FusedInc opening is born at
  the shared 6b cycle point
- the store-binding section states the structural binding (no selector
  opening; the raw Store column stays the sixth staged-value wire)
- READ_RAF_CYCLE_STAGES = 9 vs NUM_BYTECODE_VAL_STAGES = 6 made explicit
- stage-7 inputs and the verifier/prover schedule re-pointed
…catalog setup sizing

Port of #1685 (quangvdao), re-expressed over the read-raf inc dissolution
and the staged-value array refactor. Three parts:

- rename: the native committed group W_jolt becomes OneHotTrace and its
  members become columns (one_hot_trace_columns, OneHotTraceLayout/Plan/
  SetupShape, ONE_HOT_TRACE_LAYOUT, OneHotTraceCommitmentMetadata/
  SetupMetadata); LatticeIncColumns -> FusedIncColumns, FusedIncCycle ->
  FusedIncValue; advice/program objects follow (commit_advice_one_hot,
  commit_program_one_hot, program_one_hot_commitment label); prose sweeps
  in the lattice specs
- streamlined commitments: assemble_one_hot_trace feeds
  commit_one_hot_group_owned, and stage 8 opens from the commit hint
  (open_one_hot_group_from_hint) instead of retaining the full witness
- catalog setup sizing: production OneHotTrace setup envelopes computed
  from the checked-in K=16/K=256 schedule catalogs (every catalogued
  sub-shape plus exact root A/B/D footprints, with overflow and
  scalar-layout guards, ~28 ms), replacing planner-DP scans; upstream
  planner fallback retained for non-production shapes; only the fp128 D64
  full and one-hot schedule families are enabled
- bench: the sha2-chain harness times verifier preprocessing separately

Reconciliation deltas vs the source branch: FusedIncColumns keeps the
Arc'd Option<u8> lane shape from the lazy-binding perf pass; the
IncVirtualization phase and FusedIncClaimReduction member it still carried
do not return (dissolved into the read-raf); prove_stage6b takes the fused
column by mem::take.
… CI lanes, cleanups

Addresses quangvdao's pre-merge review on #1675:

- jolt-akita: derive the expected backend commitment coefficient count and
  batched proof shape from the trusted verifier setup / resolved schedule
  (shape_guard module), rejecting mismatches before any shape-backed
  allocation; require exact byte consumption on every backend
  deserialization; add malformed-shape unit and wire-level tests.
- schedules drift test: compare planner-regenerated catalogs by token
  stream instead of byte-for-byte (the emitter output is unformatted while
  checked-in modules are rustfmt-formatted outside the rustfmt::skip
  tables); report the first mismatching token with context. Run it in a
  dedicated CI lane.
- CI: lint jolt-verifier under akita,prover-fixtures (the fixture suite
  lands with the stacked prover PR); add the test-akita-schedules job.
- Drop the six accidentally committed fuzz Cargo.lock files and revert
  jolt-eval/fuzz/Cargo.lock to main (fuzz lanes regenerate lockfiles).
- specs/lattice-claims.md: status in review, link PR #1675.
markosg04 and others added 7 commits July 21, 2026 15:39
Follow-ups to quangvdao's review on #1675/#1676:

- CI: add the test-prover-akita job (release jolt-prover-legacy with the
  akita feature — muldiv, forced-K256, advice, and committed-program packed
  e2es) and the test-verifier-akita-fixtures job (release jolt-verifier with
  akita,prover-fixtures — valid-fixture acceptance plus the typed
  clear-claim, commitment-byte, proof-shape, and presence tamper sweeps),
  plus a jolt-prover-legacy akita clippy lane. Both suites verified green
  locally with the exact CI commands (442 and 84 tests respectively).
- sha2_chain_dory_perf: print the postcard-serialized proof size (the akita
  twin's verifier-native proof does not implement Serialize yet — the
  lattice stage-claim types lack derives; follow-up).
The combo cannot compile on this branch: prover-fixtures pulls in
jolt-prover-legacy, whose dory-only stage structs mismatch the akita-shaped
verifier until the packed prover lands (#1676, where the lane now lives).
- Drop assemble_one_hot_trace_columns and ColumnCells: production assembles
  OneHotTrace columns in packed.rs, and the dead copy's padding semantics
  (all-zero rows) had already diverged from the live lane-0/MSB-hot encoding.
  The encoding tests it carried now pin FusedIncValue directly.
- Drop the caller-less generate_witness arms for UnsignedIncChunk/Msb; the
  Msb arm also materialized a dense column instead of the committed K-lane
  one-hot grid. The variants now panic like their streaming twin.
- Drop the caller-less BytecodeReadRafAddressSumcheckVerifier::new_with_stages.
- Drop the zk assert in prove_packed: the module is cfg(not(zk)) and the
  workspace compile_error! already rejects zk x akita.

Gates: clippy (host / host,zk / host,akita), muldiv e2e in all three modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there seems to be a lot of runtime-gated (e.g. via self.fused_inc.is_some()) split codepaths to handle Akita in this file, and I suspect it slightly regresses the performance of the Dory path. This crate already has an akita feature, why not use that?

/// byte lanes encode padding rows as byte-0 hot, matching the committed
/// witness.
#[tracing::instrument(skip_all, name = "BytecodeReconstructionSumcheckProver::initialize")]
pub fn initialize(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function does many serial passes over the bytecode, which will be slow for large guest programs

Comment on lines +86 to +87
let mut positions = self.one_positions.clone();
positions.sort_unstable();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we just sort one_positions once up front in SparseUnitPolynomial::new?

Comment on lines +3 to +16
#[cfg(not(feature = "akita"))]
pub mod commitments;
#[cfg(not(feature = "akita"))]
pub mod configs;
pub mod manifest;
#[cfg(not(feature = "akita"))]
pub mod openings;
#[cfg(not(feature = "akita"))]
pub mod preamble;
#[cfg(not(feature = "akita"))]
pub mod proof_shape;
#[cfg(not(feature = "akita"))]
pub mod sumcheck;
#[cfg(not(feature = "akita"))]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these all intentionally cfg'd out?

Comment on lines +32 to +37
"dep:akita-config",
"dep:akita-field",
"dep:jolt-akita",
"jolt-claims?/akita",
"jolt-field/akita",
"jolt-verifier/akita",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the ? for jolt-claims but not the other deps?

- name: Test jolt-verifier with akita feature
run: cargo nextest run -p jolt-verifier --features akita --no-tests=pass --cargo-quiet

test-akita-schedules:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have "Test crates with field-inline" under this job, which belongs under test-crates

@moodlezoup

Copy link
Copy Markdown
Collaborator

Untrusted-advice scaling: a sumcheck memory cliff and ~0.5 s/prove of avoidable setup

Both issues below scale with max_untrusted_advice_size, which is a default (4096, common/src/constants.rs:29), not a cap — guests can raise it via the memory config, and the only advice e2e runs 32 bytes. Everything here fails slow, not unsound, but the degradation is silent. Numbers are from the PR branch, release build (methodology at the bottom); absolute times are machine-relative.

1. The untrusted reconstruction sumcheck densifies the full cell domain

Unlike the trusted-advice and program-image instances — which pre-fold the word dimension into the init scatter and bind a fixed 11 rounds — the untrusted instance must bind the full (byte ‖ place ‖ word) domain (11 + word_vars rounds), because its booleanity leg is nonlinear in the column and its hamming leg quantifies over every row; neither commutes with partial evaluation at r_word.

The cost driver is k_bool = EqPolynomial::evals(&r_reference) (advice_bytes.rs:164): a dense field-element table over all 2^(11 + word_vars) cells (= 256·S cells at advice size S), re-bind_parallel'd every round, alongside the dense column table:

max_untrusted_advice_size cell domain k_bool table (fp128) rounds
4 KB (default) 2^20 16 MB 20
64 KB 2^24 256 MB 24
1 MB 2^28 4 GiB 28

At the default it's tens of ms and fine; at MB-scale it's memory-disqualifying. For contrast, the Dory path stores advice as an S/8-coefficient word polynomial — this cliff is lattice-path-specific (the byte decomposition inflates the domain 2048×).

Suggested fix, structural: the untrusted byte column is a strict one-hot column (K=256 byte lanes over 8·words rows) — exactly the shape the lattice booleanity/hamming machinery in this PR already handles with K-sized pushforwards, Gruen split-eq, and lazy index columns. Routing the booleanity + hamming legs through that machinery (and pre-folding the decode leg like the trusted instance) makes the whole thing O(S) with small constants. Cheaper local fix: split-eq for k_bool plus one-hot-aware sparse early rounds, which kills the memory blowup. Interim guardrail either way: assert or document a max advice size in lattice mode, and add an e2e at the 4 KB max so the boundary is exercised.

2. Every prove with untrusted advice rebuilds the advice object's backend setup — and ~92% of it is a flavor the object never uses

commit_advice_one_hot calls transparent_object_setup(cell_vars) inline (packed.rs:440-441), and untrusted advice commits during proving, so this runs per prove. Measured (setup + commit for the singleton object, at full one-position density):

max_untrusted_advice_size setup + commit of which commit
4 KB (default) 468 ms ~5 ms
64 KB 859 ms ~40 ms
1 MB 1.96 s tens of ms

Against the ~8.2 s prove at 2^20 that's ~6% for any advice-using guest at the default shape (~20% of a 2^18 prove). Two compounding causes:

  • AkitaScheme::setup builds both backend flavors: the full-flavor setup the sparse-unit advice commit actually uses (~12 ms — SIS matrix expansion) and the one-hot flavor setup whenever max_num_vars >= one_hot_log_k, i.e. always (scheme.rs:340-352) — ~433 ms of the 468 ms, and the advice object never commits through it. AkitaSetupParams::one_hot_only exists for OneHotTrace; there's no inverse full_only for singleton objects.
  • Nothing caches the result: upstream's setup persistence is behind a disk-persistence feature jolt-akita doesn't enable, there's no in-memory memo (only the sizing envelope is memoized), and the setup depends only on (cell_vars, width=1, zero seed) — i.e. on max_untrusted_advice_size, which is preprocessing-time data.

Suggested fixes, both plumbing: (a) add a full_only setup-params variant (mirror of one_hot_only) — cuts the per-prove cost to ~15 ms by itself; (b) hoist the advice-object setup into prover preprocessing keyed on max_untrusted_advice_size. The verifier side already re-derives the same setup from public shape in akita_verifier_preprocessing, so neither changes the protocol.

(The sparse-unit commit itself is not a concern: the backend's column_sweep_sparse does one shift-accumulate per (SIS-row × one-position), so it scales with the ones, not the domain — consistent with the 5→40 ms measurements.)

Methodology: timed commit_trusted_advice_one_hot(&advice, max) — the identical commit_advice_one_hot path the prover runs for untrusted advice — across max sizes in a release build on this branch, with tracing span-close timings to split setup from commit; the padding encoding means one-position count equals max regardless of actual advice length, so commit was measured at full density.

moodlezoup added a commit that referenced this pull request Jul 22, 2026
…ics (#1675)

* feat: Akita lattice PCS integration — verifier path and shared semantics

Brings the verifier-side Akita integration onto main as one commit:
the fused increment claim pipeline (IncVirtualization as its own stage,
the stage-6b fused-inc reduction, the stage-7 lattice hamming
mega-reduction with the decode identity), MSB as a uniform one-hot
member, the native W_jolt one-hot group opened directly at one common
point behind layout-digest/metadata validation, the shared K=16/K=256
trace-length toggle, jolt-owned schedule catalogs injected through
akita's catalog hook, and the joint packed opening for auxiliary
objects. Includes the claims-level akita feature with the cfg'd
NUM_BYTECODE_VAL_STAGES const and fixed-size stage arrays, the
consolidated derive verify_clear with String stage labels, and the
append_length_prefixed transcript absorber rename (review feedback,
moodlezoup). Protocol design of the fused pipeline and native opening
by Quang Dao (quangvdao).

akita deps pin markosg04/akita @ cb13b79f (upstream main through the
quantum SIS cutover + the K16 preset now upstream as
LayerZero-Labs/akita#308); re-pin to upstream on its merge.

Minimal jolt-prover-legacy compat touches (full prover lands in the
follow-up PR): the three jolt_verifier::verify call sites drop the
removed zk argument. The akita fixture-driven tamper suite rides the
prover PR (it generates fixtures through the akita prover), so the
akita,prover-fixtures feature combo activates there.

Gates: workspace clippy strict (base, akita, prover-fixtures,
jolt-prover/kernels); muldiv e2e host + host,zk with main's prover;
verifier suites 75 base / 77 akita; modular crates green; fmt; typos.

* refactor(bytecode-read-raf): size staged values by NUM_BYTECODE_VAL_STAGES

Address review: replace the Vec-typed committed publics/inputs and the
([F; 5], bool) / ([F; 5], F) store side-channels with
[_; NUM_BYTECODE_VAL_STAGES] arrays (5 base, 6 akita). The lattice store
stage is now read_raf_row_values' sixth staged value, folded uniformly by
fold_stage_values; the stage-6b akita arm resolves StageValue(5) from the
staged array instead of a separate store fold.

Gates: clippy base/akita/zk+fixtures clean; jolt-claims+kernels+prover 191
tests; jolt-verifier 118 base (prover-fixtures) / 77 akita; muldiv e2e
host and host,zk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(akita): dissolve IncVirtualization into the bytecode read-raf

The standalone log_T-round IncVirtualization phase (between stages 5 and
6a) and the stage-6b FusedIncClaimReduction member both disappear: the
bytecode read-raf gains four fused-inc consumer val stages that discharge
the four reduced inc claims directly.

Substituting Store(j) = sum_k val_store(k) ra(k,j) (and its complement,
valid because bytecode ra is one-hot per cycle) into the fused-inc
identities FusedInc*Store = RamInc and FusedInc*(1-Store) = RdInc turns
each inc claim into a read-raf-shaped stage: a bytecode val column
(store/notstore), the producing relation's cycle point, and the shared RA
product, with one shared FusedInc cycle factor (cycle-phase degree d+1 ->
d+2). The four claims are all available before stage 6a, so the timing
pin that forced the standalone phase (its store-selector opening feeding
the read-raf input fold) is gone, and no store opening exists anywhere.

- jolt-claims: LatticeReadRafAddressPhase folds the inc claims at
  gamma^5..8 (pc/shift/entry shift to gamma^9..11); new lattice cycle
  symbolics carry the FusedInc opening; committed mode resolves the fused
  stages through the staged store val and 1 - store, so the staged-val
  wires are unchanged; READ_RAF_CYCLE_STAGES (5 base / 9 akita) sizes the
  cycle-point and cycle-eq arrays, distinct from the staged-val wire
  count; the stage-7 hamming decode leg re-points to
  FusedInc@BytecodeReadRaf (same 6b cycle point).
- jolt-verifier: the inc-virtualization stage, its proof field and claims
  block, and the fused 6b member are deleted; stage 6a wires the four inc
  claims from stages 2/4/5; stage 6b builds nine cycle points and
  resolves the fused stages from the staged store fold.

Proof shrinks by the standalone phase (log_T degree-3 round polys and two
claim cells) net of one extra coefficient per 6b round; one fewer
Fiat-Shamir stage. The prover half lands with the jolt-prover-legacy
integration (#1676).

* nit

* docs(lattice): update the claims spec for the read-raf inc dissolution

specs/lattice-claims.md still described the superseded protocol (the
standalone IncVirtualization phase and the stage-6b FusedIncClaimReduction
member). Rewritten per the review follow-up:

- scope, module inventory, and typed ids drop the two deleted relations;
  relations/read_raf.rs joins the module tree
- the inc one-hotting chain's first section now specifies the four fused-inc
  consumer val stages at gamma^5..8 with the Store substitution, and the
  second (point-alignment) section is gone — the FusedInc opening is born at
  the shared 6b cycle point
- the store-binding section states the structural binding (no selector
  opening; the raw Store column stays the sixth staged-value wire)
- READ_RAF_CYCLE_STAGES = 9 vs NUM_BYTECODE_VAL_STAGES = 6 made explicit
- stage-7 inputs and the verifier/prover schedule re-pointed

* perf(akita): one-hot trace follow-ups — naming and catalog setup sizing

Verifier-side half of the #1685 follow-ups, re-expressed over the read-raf
inc dissolution and the staged-value array refactor:

- rename: the native committed group W_jolt becomes OneHotTrace and its
  members become columns (one_hot_trace_columns, OneHotTraceLayout/Plan/
  SetupShape, ONE_HOT_TRACE_LAYOUT, OneHotTraceCommitmentMetadata/
  SetupMetadata); prose sweeps in the lattice specs
- streamlined commitments: the scheme gains commit_one_hot_group_owned and
  open_one_hot_group_from_hint so the prover commits owned columns and
  opens from the hint without retaining the witness
- catalog setup sizing: production OneHotTrace setup envelopes computed
  from the checked-in K=16/K=256 schedule catalogs (every catalogued
  sub-shape plus exact root A/B/D footprints, with overflow and
  scalar-layout guards), replacing planner-DP scans; upstream planner
  fallback retained for non-production shapes; only the fp128 D64 full and
  one-hot schedule families are enabled

The packed-prover half lands with #1676.

* review: shape-guard Akita deserialization, fix schedule drift oracle, CI lanes, cleanups

Addresses quangvdao's pre-merge review on #1675:

- jolt-akita: derive the expected backend commitment coefficient count and
  batched proof shape from the trusted verifier setup / resolved schedule
  (shape_guard module), rejecting mismatches before any shape-backed
  allocation; require exact byte consumption on every backend
  deserialization; add malformed-shape unit and wire-level tests.
- schedules drift test: compare planner-regenerated catalogs by token
  stream instead of byte-for-byte (the emitter output is unformatted while
  checked-in modules are rustfmt-formatted outside the rustfmt::skip
  tables); report the first mismatching token with context. Run it in a
  dedicated CI lane.
- CI: lint jolt-verifier under akita,prover-fixtures (the fixture suite
  lands with the stacked prover PR); add the test-akita-schedules job.
- Drop the six accidentally committed fuzz Cargo.lock files and revert
  jolt-eval/fuzz/Cargo.lock to main (fuzz lanes regenerate lockfiles).
- specs/lattice-claims.md: status in review, link PR #1675.

* fix(akita): drift-test failure via assert_eq, workspace clippy denies panic

* ci: move akita,prover-fixtures clippy lane to the prover PR

The combo cannot compile on this branch: prover-fixtures pulls in
jolt-prover-legacy, whose dory-only stage structs mismatch the akita-shaped
verifier until the packed prover lands (#1676, where the lane now lives).

---------

Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Quang Dao <qvd@andrew.cmu.edu>
All four conflicts are files where this branch extends #1675's content;
main's copies are byte-identical to the verifier branch's final state, so
this branch's versions win unchanged.
…tegration' into feat/jolt-legacy-prover-akita-integration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

implementation PR contains implementation of a spec spec Tracking issue for a feature spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants