Skip to content

refactor: complete the Akita shared-field cutover - #1

Closed
Acentelles wants to merge 6 commits into
feat/solinas-field-stackfrom
feat/akita-cutover-completion
Closed

refactor: complete the Akita shared-field cutover#1
Acentelles wants to merge 6 commits into
feat/solinas-field-stackfrom
feat/akita-cutover-completion

Conversation

@Acentelles

Copy link
Copy Markdown
Owner

Summary

DRAFT, stacked on a16z#1684 and on Akita a16z#307; cannot land before both merge. Every stacked pin carries a WARNING comment and must be re-pointed to the LayerZero-Labs/akita merge revision of a16z#307 before landing.

This PR discharges every "removed in the final migration PR" promise left in the tree by a16z#1684 (the shared-field landing) and Akita a16z#307 (the Akita-side cutover). Post-cutover, Akita's field type IS jolt_field::Fp128, so the bootstrap machinery is not replaced — it is deleted as meaningless.

Changes

  • Delete the akita bootstrap edge from jolt-field: src/akita.rs, the akita feature, and the optional akita-config/akita-field dependencies.
  • Restore JoltField's serde bounds (Field + CanonicalEncoding + WithAccumulator + Serialize + DeserializeOwned), reverting the recorded deviation that existed only because the orphan rule barred serde impls for the foreign bootstrap type.
  • Re-point the akita dependencies to the post-cutover API (Akita Refactoring get_polys_r1cs to use references instead of cloning a16z/jolt#307 head) and adapt jolt-akita plus the legacy packed prover path; akita-field disappears from the dependency graph entirely.
  • Single-identity wiring: a root [patch] table redirects Akita's jolt-field git dependency onto the workspace member, so exactly one jolt-field package identity resolves; the shared-field identity gate flips to reject every akita-field identity (structural cargo metadata check, color-independent).
  • Jolt-owned schedule catalogs regenerated and extended. Post-cutover Akita resolves runtime schedules from generated catalogs only (the planner DP is offline-only), and upstream's generated tables are gitignored bootstrap artifacts a Git-dependency checkout does not contain. The catalogs now cover the production grids plus an explicit test-support grid (vars 12–16, 1–4 polynomials) and the forced-K256 e2e band (20 vars); test fixtures moved off shapes that are no longer schedulable, with dense-only/one-hot-only setups so neither flavor is built at unsupported shapes.
  • Per-field scalar-challenge convention. The legacy transcripts hardcoded the digest-bytes-reversed (big-endian) scalar-challenge decode — BN254's convention — while the modular verifier decodes with the field's own from_scalar_challenge_bytes, unreversed little-endian for the shared Solinas Fp128. Pre-cutover the bootstrap adapter carried the reversal, so both sides agreed; the cutover silently flipped the verifier side. Both decodes consume the same 16 bytes, so the transcript stream stayed synchronized while every challenge_scalar value diverged — surfacing as a deterministic StageClaimOutputMismatch at the first batched-claim check, packed pipeline only. Fixed by giving the legacy JoltField a from_scalar_challenge_bytes whose default preserves the reversed convention (BN254/Dory byte-identical by construction), delegating the blake2b/keccak/prover-native transcripts to it, and overriding it on AkitaFp128 to the shared unreversed convention.
  • jolt-prover-legacy's akita feature now enables dep:jolt-field directly, so -p jolt-verifier --features akita builds standalone.

Testing

  • Ran tests for modified crates: jolt-prover-legacy --features akita 445/445 (including all three packed muldiv e2e variants); jolt-akita 38 passed, 3 skipped; muldiv e2e 3/3 under host and 3/3 under host,zk (the BN254 proving path is untouched).
  • cargo clippy and cargo fmt pass: fmt --check clean; clippy --all-targets -- -D warnings under host, host,zk, -p jolt-verifier --features akita, and -p jolt-prover-legacy --features akita, all with RUSTFLAGS=-Dwarnings.
  • Shared-field identity gate (post-cutover semantics): exactly one jolt-field identity, zero akita-field.

Security Considerations

  • Challenge derivation changes for the packed (Akita) pipeline only: AkitaFp128 scalar challenges now decode unreversed little-endian, aligning the legacy prover with the modular verifier and with Akita Refactoring get_polys_r1cs to use references instead of cloning a16z/jolt#307's own transcript convention. BN254 challenge derivation is byte-identical by construction (the default preserves the reversed convention), witnessed by the Dory muldiv e2e passing in both modes. Packed proofs are not byte-frozen across the cutover.
  • Schedule catalogs affect completeness, not soundness: a lookup miss is a hard error (fail-closed), and catalog identity is validated against the config policy on every lookup. One production boundary shape (16 vars, 81 polynomials — the widest K=16 group at the minimum padded trace) is unschedulable by the post-cutover planner although the pre-cutover runtime planner scheduled it; the coverage test pins it as an explicit exception that fails the moment the planner covers it, and the question is raised on the Akita Refactoring get_polys_r1cs to use references instead of cloning a16z/jolt#307 review thread.
  • Dependency-graph integrity: the identity gate enforces a single jolt-field package identity and zero akita-field, closing the dual-identity hazard the [patch] wiring exists to prevent.

Breaking Changes

  • jolt-field loses its akita feature and optional akita dependencies; JoltField regains Serialize + DeserializeOwned supertraits (types implementing the component traits without serde no longer satisfy it).
  • Packed (Akita-mode) proof transcripts change: AkitaFp128 scalar-challenge decoding switches to the shared unreversed convention, so packed proofs produced before this PR do not verify after it (and vice versa). BN254/Dory proof formats are unchanged.
  • jolt-prover-legacy's akita feature now enables dep:jolt-field.

…ield serde bounds

The temporary bootstrap adapter for the pre-cutover akita-field type
(src/akita.rs, the `akita` feature, and the optional akita-config/
akita-field dependencies) existed only so the pre-cutover Akita field
could satisfy JoltField before Akita a16z#307 rebound Akita onto the shared
jolt-field landed by Jolt a16z#1684. With Akita a16z#307 the Akita field type IS
jolt_field::Fp128 at Akita's modulus, so the edge is dead: delete it and
restore the serde bounds (Serialize + DeserializeOwned) on the JoltField
trait and its blanket impl, per the promise recorded in the trait doc and
in specs/jolt-field-rebuild.md deviation 1.

Downstream manifests drop their references to the deleted feature; the
legacy prover's own akita-config/akita-field dependency edges die with
the adapter rebind in the next commit.
Re-point the akita-* git pins from the pre-cutover revision to the
Akita a16z#307 head and adapt jolt-akita and the legacy packed prover path:
the post-cutover Akita field type IS jolt_field::Fp128, so the legacy
AkitaFp128 adapter surface shrinks accordingly, and schedule resolution
is catalog-only (the planner DP became offline-only, and upstream's own
generated tables are gitignored bootstrap artifacts that a
Git-dependency checkout does not contain).

Consequences carried here:
- Jolt-owned schedule catalogs regenerated via gen_jolt_schedules over
  the production grids plus an explicit test-support grid
  (vars 12..=16, 1..=4 polys) covering the shapes jolt-akita's own
  harnesses exercise, mirroring the test-scale rows upstream ships in
  its local catalogs.
- One production boundary shape, the widest K=16 group at the minimum
  padded trace (16 vars, 81 polys), is unschedulable by the
  post-cutover planner although the pre-cutover runtime planner
  scheduled it; the catalog coverage test pins this as an explicit
  known exception that fails the moment the planner covers it, and the
  question is raised on the Akita a16z#307 review thread.
- Test fixtures moved off shapes that are no longer schedulable
  (13-var 2-poly dense groups); purely-dense fixtures use dense-only
  setups and the grouped one-hot test a one-hot-only setup, so neither
  builds the other flavor's setup at unsupported shapes.

jolt-akita suite: 38 passed, 0 failed, 3 skipped.

WARNING: the akita pins reference the stacked-review head on the
contributor fork; re-point to the LayerZero-Labs/akita merge revision
of a16z#307 before landing.
The structural cargo-metadata check now rejects every akita-field
identity (the crate no longer exists; any occurrence means a stale
pre-cutover Akita pin in the graph) and requires exactly one jolt-field
identity, the workspace path unified with Akita's Git pin via the root
[patch] table. Workflow lane renamed to match.
…rebuild

Deviation 1 (JoltField serde bounds) and the akita cutover item move
from open to discharged-by-this-branch; the remaining-work list keeps
x86 validation, bench re-runs, and the CI SIMD lane.
…eature

The akita feature now enables dep:jolt-field directly (previously only
the prover feature did, so -p jolt-verifier --features akita had no
jolt-field to link for the rewritten adapter), and the packed test
modules qualify Ring::from_u64 at the call sites.

Known-failing on this branch, documented in the draft description:
zkvm::packed::tests::{muldiv_e2e_akita, muldiv_e2e_akita_forced_k256}
fail verification with StageClaimOutputMismatch at stage 1 — the
legacy packed stage-claim plumbing predates the post-cutover Akita
claim layout (per-group opening points, chunk-major witnesses); the
adaptation is an open item pending Akita-maintainer input. 443/445
legacy akita tests pass.
…e packed pipeline

Root cause of the packed muldiv e2e failures (StageClaimOutputMismatch
at stage 1): the legacy transcripts hardcode the scalar-challenge
convention as digest-bytes-reversed (big-endian) before field decode —
correct for BN254 — while the modular verifier decodes with the field's
own from_scalar_challenge_bytes, which for the shared Solinas Fp128 is
unreversed little-endian. Pre-cutover the akita bootstrap adapter
carried the reversed convention, so both sides agreed; the cutover made
AkitaField the shared Fp128 and silently flipped the verifier side.
Both decodes consume the same 16 bytes, so the transcript stream stayed
synchronized while every challenge_scalar VALUE diverged, surfacing as
a claim/reduction mismatch at the first batched check of the packed
pipeline only.

Fix: the legacy JoltField gains from_scalar_challenge_bytes with the
reversed convention as its default (BN254/Dory byte-identical by
construction); blake2b, keccak, and the prover-native verifier
transcripts delegate to it; AkitaFp128 overrides it to the shared
unreversed convention, matching the modular verifier.

Also: the K=256 catalog gains the forced-K256 e2e band (20 vars at
production widths) — the forced test deliberately runs the K=256
regime below its production window, which the offline-only planner
must cover explicitly.

Battery: fmt, clippy (host, host+zk, akita x2, -Dwarnings), identity
gate, muldiv 3/3 host and 3/3 zk, jolt-prover-legacy --features akita
445/445, jolt-akita 38/38.
@github-actions github-actions Bot added spec PR contains a spec implementation PR contains implementation of a spec labels Aug 6, 2026
@Acentelles

Copy link
Copy Markdown
Owner Author

Superseded: re-opened against a16z/jolt as the stacked draft.

@Acentelles Acentelles closed this Aug 6, 2026
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 PR contains a spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant