Skip to content

Refactor Field and RandomField:#142

Draft
cupicmarko wants to merge 1 commit intomainfrom
refactor/const_monty_param
Draft

Refactor Field and RandomField:#142
cupicmarko wants to merge 1 commit intomainfrom
refactor/const_monty_param

Conversation

@cupicmarko
Copy link
Copy Markdown
Contributor

  • Use ConstMontyParams and ConstMontyForm from crypto-bigint crate
  • Bump crypto-bigint version
  • Use crypto_bigint::Word for all platform-specific word sizes
  • Add F{N} aliases for Field akin to U64, U128, ... for portability
  • Remove BigInt and use crypto_bigint::Uint instead
  • Remove all Config and ConfigReference and use ConstMontyParams instead
  • Remove traits::{Uint, Int} and use crypto_bigint::{Uint, Int}
  • Use From::from instead of FieldMap::map_to_field

Introduce traits for arithmetic operations:

  • Value for Copy, Debug, ...
  • Abelian for addition
  • Monoid for multiplication

Refactor Polynomials:

  • Unify MLE for Field and Integer into single implementation

Refactor CCS:

  • Unify CCS implementation for Field and Integer into single implementation

Refactor Zip, Spartan, Sumcheck to use new Field and RandomField

* Use ConstMontyParams and ConstMontyForm from crypto-bigint crate
* Bump crypto-bigint version
* Use crypto_bigint::Word for all platform-specific word sizes
* Add F{N} aliases for Field akin to U64, U128, ... for portability
* Remove BigInt and use crypto_bigint::Uint instead
* Remove all Config and ConfigReference and use ConstMontyParams instead
* Remove traits::{Uint, Int} and use crypto_bigint::{Uint, Int}
* Use From::from instead of FieldMap::map_to_field

Introduce traits for arithmetic operations:
* Value for Copy, Debug, ...
* Abelian for addition
* Monoid for multiplication

Refactor Polynomials:
* Unify MLE for Field and Integer into single implementation

Refactor CCS:
* Unify CCS implementation for Field and Integer into single implementation

Refactor Zip, Spartan, Sumcheck to use new Field and RandomField
@cupicmarko
Copy link
Copy Markdown
Contributor Author

Pending comments in the PR for clarity

@cupicmarko
Copy link
Copy Markdown
Contributor Author

There are 3 tests in Spartan which are failing and I didn't fix them yet

Copy link
Copy Markdown
Contributor Author

@cupicmarko cupicmarko left a comment

Choose a reason for hiding this comment

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

There are some changes that introduce a lot of diffs, but are simple:

  • ZT: ZipTypes is replaced with direct use of const N: usize, const const L: usize, const K: usize, const M: usize, using a trait or struct to hold these consts would require switching to nightly

  • RandomField<'cfg, const N: usize> is replaced by RandomField<ModP, LIMBS>

  • I: Integer is often replaced by const I: usize, I was not consistent with this, but it could be refactored in future

  • Witness and Witness_Z and similar CCS structs and traits are unified

  • MLEs are unified because without config they work for any type

Comment thread src/field/biginteger.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This entire module is removed, we are using ConstMontyForm instead of BigInt in RandomField

Comment thread src/field/config.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is completely replaced by crypto_bigint::ConstMontyForm

Comment thread src/field/constant.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Most of this moved to numeric.rs

Comment thread src/field/conversion.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since there is no Config anymore, we can use plain From trait

Comment thread src/field/int.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Using crypto_bigint::Int instead

Comment thread src/field/uint.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Using crypto_bigint::Uint instead

Comment thread src/utils.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a "trick" used for P3 Merkle trees to avoid Orphan rule for crypto_bigint::Int and required trait Packable. As PackedInt is a complete new_type of Int, it has the same memory layout - size and alignment

Comment thread src/poly_f.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unified into poly

Comment thread src/poly_z.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unified into poly

Comment thread src/macros.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not needed anymore

Copy link
Copy Markdown
Contributor

@frozenspider frozenspider left a comment

Choose a reason for hiding this comment

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

Great work overall! Left some comments

#![allow(clippy::eq_op)]

use ark_std::{
use std::{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are we giving up on ark_std, or was this a changed by accident?

Comment thread src/traits.rs
+ From<Uint<N>>
+ From<Int<N>>
+ for<'a> From<&'a Self>
+ for<'a> AddAssign<&'a Self>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This already comes from Abelian

Comment thread src/transcript.rs
pub fn absorb_random_field<F: Field<LIMBS>, const LIMBS: usize>(&mut self, v: &F) {
// self.absorb(&[0x3]);
// self.absorb(&ToBytes::to_be_bytes(MOD::PARAMS.modulus().as_ref()));
// self.absorb(&[0x5]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Either dead code, or forgot to uncomment. I think it's the latter - we do need to save the modulus, otherwise I guess cheating prover can claim it to be different than the one he actually used

Comment thread src/field.rs

*self = Initialized { config, value }
}
pub trait Monty<const LIMBS: usize>: ConstMontyParams<LIMBS> {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need this wrapper trait? We don't seem to implement anything else on top of it?

Comment thread src/field.rs
Initialized { value, .. } => value,
}
}
pub(crate) type Form<MOD, const LIMBS: usize> = ConstMontyForm<MOD, LIMBS>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: The name is a bit misleading. Maybe just use a direct import?

Comment thread src/field/conversion.rs
impl<MOD: Monty<LIMBS>, const LIMBS: usize> From<isize> for RandomField<MOD, LIMBS> {
fn from(value: isize) -> Self {
// Use i64 for sign handling as specified
let magnitude = Uint::from((value as i64).abs_diff(0));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ditto

Comment thread src/field/conversion.rs
Self(Form::new(&value))
}
}
impl<MOD: Monty<LIMBS>, const LIMBS: usize /*, const N: usize*/> From<Int<LIMBS>>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(Here and below) commented dead code

Comment thread src/sumcheck/utils.rs
for _ in 0..num_products {
let num_multiplicands = rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
let num_multiplicands =
rand_range_usize(rng, num_multiplicands_range.0, num_multiplicands_range.1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We're using rand crate anyway, so I think we should use rng.random_range(num_multiplicands_range.0..num_multiplicands_range.1); and get rid of rand_range_usize

Comment thread src/zinc/utils.rs
// // Method for efficient random prime sampling not yet implemented
// // Fixing the random prime q for now
// F::C::new(get_prime::<F>(transcript))
// }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Dead code

Comment thread src/zinc/verifier.rs
ccs: &CCS_F<F>,
ccs: &CcsF<F>,
) -> Result<(), SpartanError<F>> {
let e = eq_eval(point_r, beta_s)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why does this diverge from eq_eval now?

frozenspider added a commit to NethermindEth/zinc-plus that referenced this pull request Sep 29, 2025
Instead of `N, L, K, M` usize constant bounds for `Int`s, both Zip and
linear code now operates on 4 abstract rings:
* `N => EvaluationRing` 
* `L` (previously: element width of the encoding matrix of a linear
code) is no longer a generic, instead it's made into a property of
linear code. `PrimeField` elements must be obtainable from it, but
otherwise code is free to place any constraints on the rings that it
needs to make it work.
* `K => CodewordRing`
* Added new ring `ChallengeRing`, used to generate challenges for random
linear combination of codewords. Previously it was the same as
evaluation ring.
* `M => LinearCombinationRing`

These ring aliases are implemented by `Int<_>` rings, so not much
changes from a user perspective, except you have to specify
`ChallengeRing` separately.
 
## Other major changes
* Added a new trait `MulByScalar` to represent an ability to multiply a
given ring element by `ChallengeRing` (and field elements with each
other, as they are being combined too)
* `LinearCode` no longer has generic `encode_wide`, instead 3 encode
methods are now defined as follows:
  * `encode: EvaluationRing => CodewordRing`
  * `encode_wide: LinearCombinationRing => LinearCombinationRing`
  * `encode_f: PrimeField => PrimeField`
* Tweaked `RaaCode` to have a configurable `check_for_overflows`. This
is needed to keep encoding times on par with original Zinc encoding
times - using `checked_add` adds ~20% overhead.
* All to- and from-bytes encoding is now done through a new trait
`Transcribable`. It can use whatever byte order as long as it allows
serialization and deserialization cross-platform. (Actually uses
little-endian order as being more efficient and more natural when
applied to underlying `crypto-bigint` types)
* This allows to make transcription reads/writes much more generalized,
removing a bunch of type-specific methods
  * This also made `AsWords` trait unnecessary, removed it
* Cross-platform deserialization wasn't actually tested yet, left a TODO

## Additional changes
* `ConstRing` is now auto-implemented
* `dense` multilinear polynomial module moved from `poly` to `poly::mle`
to free up module name
* `num_proximity_testing` no longer depends on `log2_q`. It was unused
previously anyway, and if we need it in future, we'd need to determine
what `log2_q` is for an arbitrary ring.

## Performance
(single-threaded)
* Encoding and MT construction is on par with original Zinc.
* Commitment overall is ~35% faster.
* Open and verify is on par with
NethermindEth/zinc#142 (which is much faster
than original Zinc)
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