Skip to content

refactor(akita-field): Land Solinas field from akita-field in Jolt's jolt-field - #307

Open
Acentelles wants to merge 11 commits into
LayerZero-Labs:mainfrom
Acentelles:feat/jolt-field-cutover
Open

refactor(akita-field): Land Solinas field from akita-field in Jolt's jolt-field#307
Acentelles wants to merge 11 commits into
LayerZero-Labs:mainfrom
Acentelles:feat/jolt-field-cutover

Conversation

@Acentelles

Copy link
Copy Markdown

Summary

This PR integrates akita-field crate into the Jolt's jolt-field crate. It updates every Akita crate, test, example, benchmark, fuzz target, and profiling workspace accordingly.

The purpose is to reduce the field arithmetic audit surface. Field arithmetic moves to jolt-field, while Akita continues to own its protocol errors, validation policy, and optimized serialization format.

Changes

  • Replace all akita_field imports and manifest dependencies with direct jolt_field imports.
  • Enable jolt-field/solinas for Akita consumers and propagate jolt-field/parallel only through Akita's existing parallel features.
  • Add the Akita-owned akita-error crate and move AkitaError into it. Convert jolt_field::FieldError to AkitaError at the protocol boundary.
  • Move the existing field serialization and validation implementations into akita-serialization/src/field_impls.rs.
  • Delete crates/akita-field, its compatibility facade, and Akita's duplicate field arithmetic benchmark.
  • Update workspace and auxiliary lockfiles, fuzz and profiling manifests, CI, the Akita Book, specifications, crate graph, and verifier documentation.
  • Remove the obsolete akita-field/jolt-compat adapter test job.

Testing

  • Ran the Akita test suite with cargo test.
  • Ran the focused serialization suite with
    cargo test -p akita-serialization.
  • cargo clippy --all --message-format=short -q -- -D warnings passes.
  • cargo fmt --all --check passes.
  • ./scripts/check-doc-guardrails.sh passes.
  • ./scripts/check-shared-field-identity.sh reports exactly one
    jolt-field identity and no akita-field package.
  • git diff --check passes.

The completed test suite includes Akita's unit, integration, end-to-end, and documentation tests. Serialization coverage checks fixed prime-field bytes, extension coefficient order, validated decoding, reported size, and round-trip behavior.

Security Considerations

This PR changes the ownership and dependency path of security-sensitive field arithmetic used by the Akita prover and verifier. It does not intentionally change the arithmetic: field moduli, canonical representations, extension bases, reductions, FFT parameters, and packed kernels come from the same implementation moved into jolt-field by a16z/jolt#1684.

Akita's optimized field serialization remains Akita-owned.

The new akita-error crate preserves the existing protocol error variants.

The package identity CI guard prevents both field implementations from re-entering the dependency graph. This PR does not intentionally change proof soundness, verifier acceptance rules, private-input handling, proof bytes, or the Akita transcript schedule.

Breaking Changes

  • Removes the public akita-field package and its jolt-compat feature.
  • Changes field type paths from akita_field::* to jolt_field::*.
  • Changes the protocol error path from akita_field::AkitaError to akita_error::AkitaError.
  • Downstream Akita workspace consumers must depend on the pinned jolt-field package and enable the required Solinas feature.

Introduce the akita-error crate owning the protocol error type
(AkitaError) ahead of the akita-field removal, with a conversion from
jolt_field::FieldError at the repository boundary.

Move the field serialization implementations into
akita-serialization/src/field_impls.rs, implementing the existing wire
traits for the shared jolt-field Solinas types. Wire bytes are
unchanged and pinned by stability fixtures.

Additive only: the pre-cutover akita-field crate remains in place and
building until the workspace cutover lands.
Replace every akita_field import with direct jolt_field imports across
source, tests, examples, and benchmarks, and replace every akita-field
manifest edge with the pinned shared jolt-field package (solinas
feature; parallel only through Akita's existing parallel features).
Protocol errors now come from akita-error.

Also delete the duplicate field arithmetic benchmark, which imports
akita_field and cannot outlive this commit; its successor lives in
jolt-field as benches/solinas_field_arith.

Mechanical rewrite only; no semantic changes. akita-field itself is
removed in the next commit.
The shared jolt-field package now owns the Solinas field stack; nothing
in the workspace depends on akita-field. Remove the crate and its
workspace membership.
Replace the jolt-compat CI job with a shared-field identity check
requiring exactly one jolt-field package identity and no akita-field in
the integrated graph. Update the crate graph, verifier contract, book
chapters, README, and specs for the jolt-field cutover.
Record the opened Jolt PR A, the bootstrap-edge retention and freeze
steps, the jolt_akita::AkitaField transparent-alias policy, and the
revised PR C scope. Re-open the adapter-deletion checklist item, which
completes only when PR C lands.
@Acentelles

Copy link
Copy Markdown
Author

We need to wait for a16z/jolt#1684 to be merged first

@quangvdao

Copy link
Copy Markdown

The architectural direction looks right: jolt-field should be the single owner of the shared arithmetic stack, while Akita retains its protocol errors, validation policy, and wire serialization. The source-level split is also sensible: delete akita-field, define AkitaError once in akita-error, and implement Akita’s serialization traits for the Jolt-owned field types in akita-serialization.

I am requesting changes because the current branch is an intermediate migration snapshot rather than a mergeable Akita change. This review is against Akita head 57276e9830d342817abc2f8e0ddda7edc38596e8, current Akita main f5c180a49a83f5ce3e8b683a34208166ffed2f66, and Jolt PR #1684 head 09b2f7b6ddd9427c756b781c39530a6c005e332d.

Required before this can merge

1. Land and freeze the Jolt field source first

This PR currently pins:

https://github.com/Acentelles/jolt.git
rev = 09b2f7b6ddd9427c756b781c39530a6c005e332d

That is appropriate for stacked review, but not as Akita’s landed production dependency. Please:

  • get Jolt #1684 reviewed and merged;
  • replace every contributor-fork pin with either the immutable upstream merge revision or a released jolt-field package;
  • regenerate all root, fuzz, and profiling lockfiles from that final identity;
  • confirm that all Akita workspaces resolve the same package ID.

If the Jolt PR changes after this review, please identify the arithmetic/trait delta from 09b2f7b6… rather than silently advancing the pin.

2. Rebase onto current Akita main

The PR merge base is 3b674abb66186037745af256e6a20d60bdcd7e89. Current main has since landed #302 and #294. A merge simulation reports 35 conflicts, including config, planner, setup, prover, verifier, and setup-contribution files.

Please rebase after the Jolt source is frozen and preserve the post-#294 architecture. The result should apply the field-ownership cutover to the current APIs rather than restoring pre-#294 files while resolving conflicts.

Because this affects hundreds of generic bounds and imports, please inspect the final main...HEAD diff again after the rebase; validation on the present pre-rebase tree will not be sufficient evidence for the final tree.

3. Fix the shared-field identity guard

The new identity script fails under the workflow’s actual environment. CI sets CARGO_TERM_COLOR=always, but the script only removes an uncolored (*) marker. I reproduced:

CARGO_TERM_COLOR=always scripts/check-shared-field-identity.sh
error: expected exactly one jolt-field package identity, found 2

The two reported lines are the same package identity; the second merely has an ANSI-colored (*). With CARGO_TERM_COLOR=never, the script passes.

Please make this check structural and color-independent. Prefer parsing cargo metadata package IDs; at minimum invoke Cargo with --color never before parsing. Add a regression test that runs with the repository’s CARGO_TERM_COLOR=always setting.

The corresponding check in Jolt #1684 has the same defect and is currently red.

4. Add cross-revision golden compatibility fixtures

The new serialization module has useful fixed prime-byte and extension coefficient-order tests, and the existing round-trip tests continue to run against the new types. However, that is not enough to substantiate the broader claims that proof bytes, setup bytes, transcript challenges, and validation behavior are unchanged.

Please add fixtures generated by the pinned pre-cutover Akita revision and consumed by the post-cutover implementation. They should cover, at minimum:

  • canonical boundary encodings for every production prime-field width;
  • extension coefficients in the exact Akita wire order;
  • validated rejection of noncanonical encodings;
  • deterministic commitment/setup serialization;
  • at least one deterministic complete proof serialization;
  • transcript absorption and resulting challenge bytes for a fixed schedule.

The expected bytes should come from the old implementation or a checked-in pre-cutover artifact, not be generated and blessed by the new implementation in the same test. If any of these are intentionally deferred, narrow the PR’s compatibility claims accordingly.

5. Make the staged Jolt state and cleanup obligation explicit

Jolt #1684’s current description says that jolt-field/src/akita.rs, the akita feature, and Akita dependencies are deleted. At its actual head they are intentionally retained as bootstrap wiring. jolt_akita::AkitaField also still routes through the pre-cutover Akita configuration.

The latest Akita spec explains the staged state more accurately. Please:

  • correct the Jolt PR description to match its actual head;
  • update the spec metadata to name Akita PR B as refactor(akita-field): Land Solinas field from akita-field in Jolt's jolt-field #307;
  • link the issue or PR that will implement Jolt PR C;
  • describe how PR C will patch Akita’s immutable jolt-field dependency back to Jolt’s workspace member without producing two Cargo package identities;
  • keep the adapter/bootstrap deletion acceptance item unchecked until PR C actually lands.

This cleanup should be treated as part of the migration, not an optional later refactor.

6. Produce fresh final-head validation

Akita #307 currently has only external security scans attached; its normal format, clippy, test, documentation, and shared-field jobs have not run on this head. Jolt #1684 also currently has five failed checks:

  • shared-field identity;
  • Criterion benchmark feature selection;
  • jolt-field fuzz compilation;
  • Taplo formatting;
  • typos.

Please make the Jolt prerequisite green, then rerun Akita validation on the rebased tree with the final upstream dependency:

  • cargo fmt --all --check
  • cargo clippy --all --message-format=short -q -- -D warnings
  • cargo test
  • ./scripts/check-doc-guardrails.sh
  • the corrected shared-field identity check
  • git diff --check

Please also report the exact final Akita and Jolt SHAs. Results from 57276e98 or from the contributor-fork field identity should not be carried forward after the rebase or dependency replacement.

What does not need changing

I do not think we should retain an akita-field re-export facade or compatibility wrapper. Akita explicitly makes no backward-compatibility guarantee, and direct jolt_field::* imports are the cleaner final API.

Likewise, keeping serialization and AkitaError on the Akita side is the correct ownership boundary. The requested changes are about making the cross-repository landing atomic enough to audit, proving byte compatibility, and ensuring the final dependency graph—not reversing the architecture.

Rebind every crate from the pre-consolidation 46-trait jolt-field surface
(rev 09b2f7b6) to the rebuilt 15-trait spine, and bump all 18 manifest pins
to rev 46e2ce8f7525364c69b5534d2d3918bdcf0684d3 (feat/jolt-field-replacement,
pushed to Acentelles/jolt as feat/solinas-field-stack).

Mapping applied (old -> new):
- FieldCore -> Field; RingCore -> Ring; FromPrimitiveInt/Invertible/
  RandomSampling/HalvingField absorbed into Ring/Field (same method names).
- CanonicalField -> Field + CanonicalEncoding: modulus_bits() ->
  MODULUS_BITS, from_canonical_u128_{checked,reduced} ->
  from_u128_{checked,reduced}; generic to_canonical_u128() ->
  to_u128_checked().expect(...) (65 call sites); concrete Fp128 call sites
  keep the inherent to_canonical_u128.
- CanonicalBytes/ReducingBytes/FixedByteSize/CanonicalU64/
  CanonicalBitLength/TranscriptChallenge -> CanonicalBytes +
  CanonicalEncoding (transcript byte behavior unchanged).
- PseudoMersenneField -> PseudoMersenne (MODULUS_OFFSET -> OFFSET,
  MODULUS_BITS now on CanonicalEncoding).
- LiftBase/MulBase/FrobeniusExtField -> ExtField (EXT_DEGREE -> DEGREE);
  FpExt2Config -> Ext2Config; FpExt4MulBackend/FpExt8MulBackend -> the
  PseudoMersenne ext hooks.
- HasUnreducedOps + HasWide -> Unreduced (MulU64Accum -> SmallProduct,
  ProductAccum -> Product, mul_to_product_accum -> mul_unreduced,
  reduce_product_accum -> reduce_product, reduce_mul_u64_accum ->
  reduce_small_product, mul_small_to_wide -> scale_wide,
  DELAYED_PRODUCT_SUM_IS_EXACT -> SUM_IS_EXACT); ReduceTo dropped in favor
  of Unreduced::reduce_wide; HasOptimizedFold -> Fold (FoldCtx -> Ctx,
  precompute_fold -> precompute).
- packed::PackedField/HasPacking -> Packed/WithPacking (root re-exports);
  jolt_field::parallel -> jolt_field::solinas::parallel;
  jolt_field::unreduced::*/packed::* -> crate root.

Ported akita-side (deleted from jolt-field with no replacement):
- akita-algebra gains `fft` (mixed-radix smooth-domain FFT, RS-extend) and
  the SmoothFftField trait with the Prime128Offset2355/Prime128OffsetA7F7
  SMOOTH_OMEGA literals, taken verbatim from the pre-rebuild jolt-field and
  rebound to the new spine; akita-pcs re-exports it (`akita_pcs::fft`).
- CyclotomicRing: RandomSampling impl became an inherent `random`; the Ring
  impl now embeds integers into the constant coefficient (new Ring
  requirement).
- WideCyclotomicRing::reduce now takes F: Unreduced<Wide = W>.
- akita-pcs no longer re-exports the jolt-field `packed`/`unreduced`
  modules or BalancedDigitLookup (module paths gone; the digit LUT is the
  free fn jolt_field::balanced_digit_lut; nothing consumed them).
- The LossyField delayed-product-sum test fixture implements the fused
  Ring/Field/Unreduced traits (including the previously absent Wide shape).

Validation (rustc 1.95.0, gates that are green at the base commit):
- cargo check --workspace --all-targets --no-default-features
  --features parallel: clean.
- cargo clippy --all --all-targets --no-default-features
  --features parallel -- -D warnings: clean.
- cargo fmt --all --check: clean.
- scripts/check-rust-file-lines.sh --no-baseline: pass.
- cargo nextest run --lib --bins --tests --cargo-profile ci-test
  --no-default-features --features parallel:
  local-path override: 1038 tests run: 1038 passed (15 slow), 14 skipped
  real git pin:        1038 tests run: 1038 passed (14 slow), 14 skipped
- fuzz workspace and profile/akita-recursion glue+artifact cargo check:
  clean (guest/host not built; they need the RISC-V jolt toolchain).

Pre-existing breakage NOT introduced here (identical at base 57276e9):
the `disk-persistence` feature fails to compile (missing AkitaDeserialize
bound in akita-setup::load_prover_setup) and the plain
--no-default-features config fails in akita-prover ring_switch/finalize.rs
(E0282 type inference in the non-parallel arm). Both fail at the same
points before and after this change.
46e2ce8f7 -> 505d8ebcb (a merge of jolt main that does not touch
crates/jolt-field; content-identical field crate, pin now matches the
PR head exactly). Workspace check green against the new rev.
Merges 25 upstream commits (e7aa598..e5351ad: chunk-major witnesses,
bounded per-matrix ring-dimension search, per-group opening points,
compressed-commitment kernels, fold-path cutover, mixed-D multi-group
openings, typed fold schedules, akita-schedules runtime resolution,
Stage 1/2 sum-check unification, exact large-digit NTT infrastructure,
Quantum128BitADPS16 SIS cutover, and more) into the jolt-field cutover
branch.

Resolution policy: for files where our side's changes were purely the
jolt-field rebind renames, the upstream version was taken and the
rebind mapping (35c9f6d) was re-applied mechanically: akita_field ->
jolt_field paths, FieldCore/RingCore/FromPrimitiveInt/Invertible/
RandomSampling/HalvingField -> Field/Ring, CanonicalField -> Field +
CanonicalEncoding (modulus_bits() -> MODULUS_BITS, generic
to_canonical_u128() -> to_u128_checked().expect(...)),
PseudoMersenneField -> PseudoMersenne (MODULUS_OFFSET -> OFFSET),
HasUnreducedOps/HasWide -> Unreduced (MulU64Accum -> SmallProduct,
ProductAccum -> Product, method renames), ReduceTo ->
Unreduced::reduce_wide, HasOptimizedFold -> Fold, LiftBase/MulBase/
FrobeniusExtField -> ExtField (EXT_DEGREE -> DEGREE), AkitaError ->
the akita-error crate, akita_field::parallel ->
jolt_field::solinas::parallel. Files upstream deleted or moved (the
planner generated tables, the akita_stage1/akita_stage2 sumcheck
modules, the direct/quotient proof paths, the setup_contribution
split) follow upstream's structure with the moved content rebound.

Upstream deltas reconciled against the deleted akita-field crate:
- AkitaError::UnsupportedSchedule (new upstream variant) is ported to
  akita-error.
- cfg_try_fold_reduce! (new upstream macro in akita_field::parallel)
  is ported to akita-algebra, since the pinned jolt-field does not
  provide it; the two akita-types consumers import it from there.
- akita-verifier gains an optional rayon dependency under its parallel
  feature because jolt-field's cfg_join! expands to a bare rayon::join
  (upstream solved this inside akita-field with a __rayon_join
  re-export); machete-ignored with a comment.
- Upstream's packed fp64 kernel/test renames (reduce128_small_k ->
  reduce128_sub_word etc.) are cosmetic-only and the code now lives in
  jolt-field; nothing to port.
- akita-schedules' generated fp*.rs tables are no longer checked in
  upstream; they are bootstrapped via
  scripts/generate-schedule-tables.sh (gitignored) and were
  regenerated here after the rebind.

Manifest edits: upstream's new akita-field dependency edges in
akita-config, akita-planner, and akita-schedules are rebound to the
shared jolt-field pin (rev 505d8ebc) plus akita-error; the resurrected
field_arith bench section is dropped (the bench moved to jolt-field);
the recursion guest gains the akita-error path dep its AkitaError
import now needs; AGENTS.md drops the stale
`cargo clippy -p akita-field --features jolt-compat` line;
book/src/foundations/spec-index.md keeps `jolt-field-unification` in
the live-spec list; docs/crate-graph.md planner/schedules bullets say
jolt-field.

Validation (rustc 1.95.0):
- cargo fmt --all --check: clean.
- cargo clippy --all --all-targets --no-default-features
  --features parallel -- -D warnings: clean.
- cargo nextest run --cargo-profile ci-test --no-default-features
  --features parallel:
  Summary [ 184.916s] 1072 tests run: 1072 passed (5 slow), 19 skipped
  (baseline before the merge: 1038 passed, 14 skipped; upstream added
  the difference).
- scripts/check-shared-field-identity.sh: pass.
- scripts/check-rust-file-lines.sh --no-baseline: pass (512 files).
- profile/akita-recursion glue+artifact+host cargo check: clean (the
  guest still needs the RISC-V jolt toolchain, unchanged); fuzz not
  re-checked. The two configs pre-broken at both bases
  (--features parallel,disk-persistence; bare --no-default-features)
  remain skipped.
The previous cargo tree text parse broke under CARGO_TERM_COLOR=always
(the workflow's actual environment): the colorized (*) dedup marker
survived the sed strip, so one package identity counted as two. Package
IDs from cargo metadata are rendering-independent; the worst-case color
environment is now forced inside the script as a regression guard.
Matches the same fix in Jolt's copy of the check.
@Acentelles
Acentelles marked this pull request as ready for review August 4, 2026 14:49
@Acentelles

Copy link
Copy Markdown
Author

Saving the merge conflicts, I've addressed your comments @quangvdao

Merges LayerZero-Labs/akita main at 0f49d0a (heterogeneous commitment
groups, chunk-major witnesses, public-claim binding, flattened public
matrix with exact NTT caches, compressed prefix payloads, x86 CRT/NTT
kernels, planner schedule-entry restore) into the jolt-field cutover.

Resolution policy: upstream semantics with post-cutover names
(jolt_field traits, akita_error::AkitaError). akita-field stays
deleted; its one upstream change in this range, exact-uniform canonical
rejection sampling (03f2908), is ported to the shared jolt-field as
Acentelles/jolt@eb93a031, and every manifest and lockfile pin moves to
that revision (the previous pin 505d8ebc is no longer fetchable after
the PR A rebuild).

Workspace check, clippy -D warnings, the full test suite, and the
shared-field identity gate all pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants