refactor(jolt-field): Land Solinas field from akita-field in jolt-field#1684
Open
Acentelles wants to merge 13 commits into
Open
refactor(jolt-field): Land Solinas field from akita-field in jolt-field#1684Acentelles wants to merge 13 commits into
akita-field in jolt-field#1684Acentelles wants to merge 13 commits into
Conversation
Move the Solinas field stack from Akita revision 3b674abb66186037745af256e6a20d60bdcd7e89 into jolt-field behind a new `solinas` feature: 32/64/128-bit pseudo-Mersenne prime fields, degree 2/4/8 extension fields, packed NEON/AVX2/AVX-512 backends, unreduced accumulators, smooth-domain FFT helpers, and a `parallel` feature gating rayon. Jolt's existing capability traits remain canonical; Solinas-only operations live in narrow capability traits in solinas_traits.rs. FieldError carries field-layer input and size failures only. The temporary `akita` feature, its optional akita-config/akita-field git-pinned dependencies, and src/akita.rs are retained as a bootstrap edge so the current jolt-akita revision keeps building; they are removed in the final migration PR.
Move the Criterion field-arithmetic benchmark from Akita as benches/solinas_field_arith (with plonky3 baselines as dev-only comparisons) and add a Solinas arithmetic fuzz target to the field fuzz workspace.
New field-stack job checks jolt-field with no backend, BN254, Solinas, and combined backends, runs the Solinas test suite, and enforces the shared field package identity: exactly one jolt-field in the workspace graph, with the pre-cutover akita-field allowed only from the immutable bootstrap Git pin. The final migration PR tightens the check to reject every akita-field identity.
Contributor
|
Warning This PR has more than 500 changed lines and does not include a spec. Large features and architectural changes benefit from a spec-driven workflow. If this PR is a bug fix, refactor, or doesn't warrant a spec, feel free to ignore this message. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
jolt-field
jolt-fieldakita-field in jolt-field
7 tasks
Acentelles
marked this pull request as ready for review
July 17, 2026 01:49
Author
|
Once LayerZero-Labs/akita#307 is merged, we can remove the temporary akita-field compatibility dependency from jolt-field and change |
markosg04
self-requested a review
July 17, 2026 01:59
Plan to shrink jolt-field from 46 public traits to at most 22: delete dead surface, merge single-purpose capability traits, adopt the arkworks/bn254.rs per-type impl style, and move wire serialization of the Solinas types to serde + bincode. Supersedes the granularity of unify-field-hierarchy.md while preserving its layering invariants.
Remove traits with no generic consumer in this workspace or akita: - SignedScalarAccumulator/WithSmallScalarAccumulator and SignedProductAccumulator/WithSignedProductAccumulator families, including the Fr and Naive implementations and the two bn254_ops kernels only they used; the Field umbrella no longer requires them - ExtensionCoeff (single blanket impl; bounds inlined at use sites) - BalancedDigitLookup (replaced by the free fn balanced_digit_lut) - SmoothFftField and fft.rs (no consumer outside their own tests) MontgomeryConstants is retained pending confirmation that no out-of-tree GPU backend consumes it.
…phase 2) AdditiveAccumulator and RingAccumulator were only ever implemented and consumed together (WideAccumulator, NaiveAccumulator); merge them into a single Accumulator trait carrying add/merge/reduce plus the fmadd family. WithAccumulator moves into accumulator.rs and now supertraits RingCore + FromPrimitiveInt, which its associated type already implied. Because WithAccumulator now guarantees Accumulator<Element = Self> by declaration, the 18 'Accumulator: RingAccumulator<Element = F>' where-clauses across jolt-crypto, jolt-blindfold, and jolt-verifier are redundant and removed.
…rmat (spec phase 3) Merge the fine-grained capability traits into cohesive ones: - FieldCore absorbs Invertible (inverse, inv_or_zero) and RandomSampling - FromPrimitiveInt gains a RingCore supertrait and absorbs the MulPow2 and MulPrimitiveInt default-method helpers - new CanonicalRepr (the Fiat-Shamir transcript surface) replaces CanonicalBytes, ReducingBytes, FixedByteSize, FixedBytes<N>, CanonicalU64, CanonicalBitLength, and TranscriptChallenge; per-type challenge derivations (Fr's masked 125-bit path) move over verbatim - the crate root shrinks from 15 one-trait micro-files to algebra.rs, canonical.rs, accumulator.rs, and field.rs Wire serialization of the Solinas types is now serde + bincode, mirroring Fr's existing canonical [u8; N] pattern: prime fields encode as exactly NUM_BYTES bytes with checked canonical deserialization, extension fields as [F; K]. Fiat-Shamir bytes stay on CanonicalRepr's explicit encoding, never bincode. New serde_roundtrip tests pin per-element sizes, single length prefixes on vectors, and rejection of non-canonical encodings, so proof size cannot grow. The mersenne61_compat and Gf2 compatibility tests keep passing with merged bounds and no arkworks dependency.
…phase 4) - ExtField<F> absorbs LiftBase (lift_base), MulBase (mul_base), and FrobeniusExtField (frobenius_pow / frobenius_inv_pow); one trait now carries the full extension contract. The FpExt2/4/8 impls require a pseudo-Mersenne base, which every current base is. - FpExt4MulBackend and FpExt8MulBackend merge into a single ExtMulBackend with the same three implementors; Fp32 keeps its fused delayed-reduction quartic overrides. - The degree-4 mul/square schedule now exists once: the packed PackedField defaults call the shared fp_ext4_mul_coeffs / fp_ext4_square_coeffs (previously copy-pasted verbatim), matching how degree-8 already shares its schedule.
Every concrete type's trait surface now lives in its own file, with three shared macros carrying the mechanical expansions: - impl_native_ring_algebra! / impl_native_additive! (new native_algebra.rs) replace the three per-module native_algebra.rs side-files, including the 226 hand-written lines in ext/; invocations sit in each type's file - impl_prime_ops! collapses the 30 hand-duplicated operator impl blocks across Fp32/Fp64/Fp128 into 3 invocations; the *_raw reduction kernels stay hand-written per type - select_packing! collapses the three near-identical cfg cascades that choose the packed backend per prime width - impl_prime_native_capability! invocations move into the per-type files Also finishes the disposition table: ScaleI32 merges into ReduceTo, PackedValue merges into PackedField, and jolt-field's OptimizedMul is deleted outright (jolt-prover-legacy has its own identical trait; the jolt-field copy had no consumer). jolt-field is now at 22 public traits. Criterion solinas_field_arith before/after shows no regression on any of the 20 benchmarks.
The fuzz crate's target/ directory (630 generated files) was accidentally committed in d8b2830. Remove it from the index and ignore it going forward. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- repair the from_bytes fuzz target's duplicate import (the fuzz crate is its own workspace, so workspace clippy never compiles it) - dedupe mechanical-rename residue bounds in the solinas benches and jolt-sumcheck's SumcheckScalar - move the Solinas prime traits (CanonicalField, HalvingField, PseudoMersenneField, balanced_digit_lut) from the crate root into prime/traits.rs per the spec's layout - amend the spec: OptimizedMul was deleted as a duplicate of jolt-prover-legacy's own trait rather than kept; record the actual Solinas-trait location and the montgomery_constants.rs remainder
Acentelles
force-pushed
the
feat/solinas-field-stack
branch
from
July 22, 2026 20:28
8d58e10 to
9aff1cd
Compare
Standard-mode muldiv proofs from this branch and main are identical in all 63,371 bytes when both builds share a Dory URS, and the main-built verifier accepts the branch-built proof. cfg(test) builds randomize the URS per process (DoryGlobals::configure_test_cache_root), so the comparison requires pinning a shared URS cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR does two things toward a single audited field implementation:
akita-fieldinjolt-field: the 32-, 64-, and 128-bit prime fields, theFpExt2/FpExt4/FpExt8extension towers, NEON/AVX2/AVX-512 packed backends, unreduced accumulator paths, tests, fuzz coverage, and benchmarks. One implementation, one Rust type identity: Akita consumesjolt-fielddirectly, with no dependency back except a temporary feature-gated bootstrap adapter (see Changes).specs/consolidate-field-traits.md. The import brought the crate to 46 public traits across 82 files, with 15 one-trait-per-file micro-files at the root. The consolidation reduces this to 22 public traits in four root trait modules (algebra.rs,canonical.rs,accumulator.rs,field.rs) plus the feature-gated backend modules, switches Solinas wire serialization to serde + bincode, and adopts the explicitarkworks/bn254.rsper-type layout. This is a refactor of trait boundaries, not arithmetic.jolt-fielddestination (post-consolidation)crates/akita-field/src/traits.rssrc/algebra.rs,src/canonical.rs,src/accumulator.rs,src/field.rs,src/solinas_traits.rscrates/akita-field/src/prime/src/prime/crates/akita-field/src/ext/src/ext/crates/akita-field/src/packed/src/packed/crates/akita-field/src/unreduced/src/unreduced/crates/akita-field/src/fft.rscrates/akita-field/src/parallel.rssrc/parallel.rscrates/akita-pcs/benches/field_arith.rsandfield_arith/benches/solinas_field_arith.rsandbenches/solinas_field_arith/fuzz/fuzz_targets/solinas_field_arith.rsChanges
Solinas import
solinasandparallelfeatures tojolt-fieldwhile retaining the existing BN254 default.AkitaSerialize, no Akita validation policy or protocol framing. AddFieldErrorfor backend-independent input and shape failures; Akita converts it at its repository boundary.src/akita.rsand theakitafeature as a temporary, feature-gated bootstrap so the adapter stays buildable until the Akita cutover; both are removed in the final migration PR.akita-config/akita-fieldare optional dependencies behind that feature only.jolt-akitato the current Akita runtime ring dimension, opening claim, setup, commitment, and batching APIs.jolt-field, add a Solinas arithmetic fuzz target, and record file-level source provenance.scripts/check-shared-field-identity.sh.Trait consolidation (spec phases 1–5)
SignedScalarAccumulator/WithSmallScalarAccumulator,SignedProductAccumulator/WithSignedProductAccumulator),ExtensionCoeff,ScaleI32,BalancedDigitLookup(now a free function), andSmoothFftFieldtogether with the 1,100-linefft.rs.AdditiveAccumulator+RingAccumulatorinto a singleAccumulator.FieldCoreabsorbsInvertibleandRandomSampling;FromPrimitiveIntabsorbsMulPow2andMulPrimitiveInt; a newCanonicalReprreplaces the seven byte/introspection/challenge traits (CanonicalBytes,ReducingBytes,FixedByteSize,FixedBytes<N>,CanonicalU64,CanonicalBitLength,TranscriptChallenge). Solinas wire serialization becomes serde + bincode, matching the existing serde implementations onarkworks/bn254.rs; Fiat-Shamir transcript bytes keep the explicit canonical little-endian encoding and never go through bincode.ExtField<F>absorbsLiftBase,MulBase, andFrobeniusExtField;FpExt4MulBackend+FpExt8MulBackendmerge into oneExtMulBackend;PackedFieldabsorbsPackedValue; the degree-4 mul/square schedule now exists in exactly one place, shared between scalar and packed backends.implor a one-line shared-macro invocation; the threenative_algebra.rsside-files are replaced by one shared macro module.BN254 is untouched by the consolidation:
Fr's serde and byte encodings, the Fiat-Shamir stream, and static dispatch are unchanged (spec invariants 1 and 6).MontgomeryConstantsis retained pending confirmation that no out-of-tree GPU/Metal consumer exists (spec OPEN item; 22 traits become 21 when resolved).Testing
muldive2e passes in--features hostand--features host,zkafter the consolidation.jolt-fieldfeature matrix passes: backend-free (--no-default-features), BN254-only, Solinas-only, combined, and Solinas-plus-parallel.mersenne61_compat(injolt-sumcheck) compiles and passes againstjolt-field --no-default-featureswith bounds updated to the merged traits: the slim hierarchy remains implementable without arkworks.Fp32/Fp64/Fp128/FpExt2/FpExt4/FpExt8; serialized-size tests assert each element encodes to exactlyNUM_BYTESbytes and aVecofCanonicalReprchallenge derivation matches the previousTranscriptChallengebehavior onFr(identical bytes in, identical element out).solinas_field_arithfuzz target and both Criterion benches build; bench results before/after the consolidation are within noise.scripts/check-shared-field-identity.shreports onejolt-fieldidentity and noakita-fieldin the integrated dependency graph.muldivproof against a main-built proof: identical in all 63,371 bytes, proving is deterministic run-to-run, and the main-built verifier accepts the branch-built proof. (Reproduction note:cfg(test)builds randomize the Dory URS per process viaDoryGlobals::configure_test_cache_root, so cross-process comparison requires pinning both builds to a shared URS cache.)Security Considerations
This change touches security-sensitive field arithmetic used by the PCS and therefore affects the trusted audit surface for prover and verifier correctness. The intended arithmetic change is zero across both stages: moduli, canonical representations, extension bases, reduction algorithms, and packed kernel behavior are preserved, and architecture-specific arithmetic kernels (NEON, AVX2, AVX-512) are moved without semantic modification.
For the import stage, 22 of the imported files are byte-for-byte copies of their Akita sources and 38 are adapted imports; the main semantic adaptations are removal of Akita serialization bounds and replacement of protocol-level
AkitaErrorwith the narrowerFieldError. The consolidation stage then rewrites trait boundaries only: merges are compile-time renames,#[inline]discipline is carried over onto both trait methods and impls, and all dispatch remains static. The net effect is a smaller audit surface: 46 public traits become 22, and the crate root drops from 15 trait micro-files to 4 trait modules.Breaking Changes
akita-fieldfrom Jolt's dependency graph (jolt-fieldkeeps optionalakita-*dependencies behind the temporaryakitabootstrap feature only).Invertible,RandomSampling,CanonicalBytes,TranscriptChallenge,LiftBase,MulBase,FrobeniusExtField,AdditiveAccumulator,PackedValue, ...) must rebind to the survivors (FieldCore,CanonicalRepr,ExtField,Accumulator,PackedField, ...). Akita adapts at its next pin bump.fft.rsandSmoothFftFieldfromjolt-field.Frencodings unchanged).