diff --git a/Cargo.toml b/Cargo.toml index bbd0bfdf..26717081 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ unroll = "0.1.5" anyhow = "1.0.40" serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.11.1" -num_bigint = "0.2.3" +num-bigint = "0.2.3" [profile.release] opt-level = 3 diff --git a/src/field/cosets.rs b/src/field/cosets.rs index 8c1a03d6..58c8b838 100644 --- a/src/field/cosets.rs +++ b/src/field/cosets.rs @@ -1,27 +1,15 @@ -use num_bigint::BigUInt; -use crate::field::field::{Field, FieldOrder}; +use num_bigint::BigUint; +use crate::field::field::Field; /// Finds a set of shifts that result in unique cosets for the multiplicative subgroup of size /// `2^subgroup_bits`. pub(crate) fn get_unique_coset_shifts(subgroup_size: usize, num_shifts: usize) -> Vec { - match F::ORDER { - FieldOrder::U64(order) => { - // From Lagrange's theorem. - let num_cosets = (order - 1) / (subgroup_size as u64); - assert!( - num_shifts as u64 <= num_cosets, - "The subgroup does not have enough distinct cosets" - ); - }, - FieldOrder::Big(order) => { - // From Lagrange's theorem. - let num_cosets = (order - 1) / (subgroup_size as BigUInt); - assert!( - num_shifts as BigUInt <= num_cosets, - "The subgroup does not have enough distinct cosets" - ); - } - } + // From Lagrange's theorem. + let num_cosets = (F::ORDER - 1u32) / (subgroup_size as u32); + assert!( + BigUint::from(num_shifts) <= num_cosets, + "The subgroup does not have enough distinct cosets" + ); // Let g be a generator of the entire multiplicative group. Let n be the order of the subgroup. // The subgroup can be written as . We can use g^0, ..., g^(num_shifts - 1) as our diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 9d38ff1a..2d635ae1 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use num_bigint::BigUint; use num::Integer; use serde::{Deserialize, Serialize}; @@ -12,6 +13,8 @@ use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::extension_field::{Extendable, Frobenius}; use crate::field::field::Field; +const FIELD_ORDER: u64 = 18446744071293632513; + /// EPSILON = 9 * 2**28 - 1 const EPSILON: u64 = 2415919103; @@ -142,11 +145,11 @@ impl Field for CrandallField { const ZERO: Self = Self(0); const ONE: Self = Self(1); const TWO: Self = Self(2); - const NEG_ONE: Self = Self(Self::ORDER - 1); + const NEG_ONE: Self = Self(FIELD_ORDER - 1); - const ORDER: u64 = 18446744071293632513; + const ORDER : BigUint = BigUint::from(FIELD_ORDER); const TWO_ADICITY: usize = 28; - const CHARACTERISTIC: u64 = Self::ORDER; + const CHARACTERISTIC: u64 = FIELD_ORDER; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(5); const POWER_OF_TWO_GENERATOR: Self = Self(10281950781551402419); @@ -170,7 +173,7 @@ impl Field for CrandallField { // Based on Algorithm 16 of "Efficient Software-Implementation of Finite Fields with // Applications to Cryptography". - let p = Self::ORDER; + let p = FIELD_ORDER; let mut u = self.to_canonical_u64(); let mut v = p; let mut b = 1u64; @@ -228,8 +231,8 @@ impl Field for CrandallField { fn to_canonical_u64(&self) -> u64 { let mut c = self.0; // We only need one condition subtraction, since 2 * ORDER would not fit in a u64. - if c >= Self::ORDER { - c -= Self::ORDER; + if c >= FIELD_ORDER { + c -= FIELD_ORDER; } c } @@ -336,7 +339,7 @@ impl Neg for CrandallField { if self.is_zero() { Self::ZERO } else { - Self(Self::ORDER - self.to_canonical_u64()) + Self(FIELD_ORDER - self.to_canonical_u64()) } } } @@ -348,7 +351,7 @@ impl Add for CrandallField { #[allow(clippy::suspicious_arithmetic_impl)] fn add(self, rhs: Self) -> Self { let (sum, over) = self.0.overflowing_add(rhs.0); - Self(sum.overflowing_sub((over as u64) * Self::ORDER).0) + Self(sum.overflowing_sub((over as u64) * FIELD_ORDER).0) } } @@ -371,7 +374,7 @@ impl Sub for CrandallField { #[allow(clippy::suspicious_arithmetic_impl)] fn sub(self, rhs: Self) -> Self { let (diff, under) = self.0.overflowing_sub(rhs.to_canonical_u64()); - Self(diff.overflowing_add((under as u64) * Self::ORDER).0) + Self(diff.overflowing_add((under as u64) * FIELD_ORDER).0) } } diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 256803ab..9ed3a1ee 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -3,6 +3,7 @@ use std::hash::Hash; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use num_bigint::BigUint; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -51,9 +52,8 @@ impl Field for QuadraticCrandallField { const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]); const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]); - const CHARACTERISTIC: u64 = CrandallField::ORDER; - // Does not fit in 64-bits. - const ORDER: u64 = 0; + const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC; + const ORDER: BigUint = CrandallField::ORDER * CrandallField::ORDER; const TWO_ADICITY: usize = 29; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(6483724566312148654), diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 9390c521..16a11b52 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -3,6 +3,8 @@ use std::hash::Hash; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use num::traits::Pow; +use num_bigint::BigUint; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -73,9 +75,9 @@ impl Field for QuarticCrandallField { CrandallField::ZERO, ]); - const CHARACTERISTIC: u64 = CrandallField::ORDER; + const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC; // Does not fit in 64-bits. - const ORDER: u64 = 0; + const ORDER: BigUint = CrandallField::ORDER.pow(4); const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(12476589904174392631), diff --git a/src/field/field.rs b/src/field/field.rs index 4928ffb7..552c8f54 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -4,8 +4,8 @@ use std::hash::Hash; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use num_bigint::BigUInt; -use num::Integer; +use num_bigint::BigUint; +use num::{Integer, Zero}; use rand::Rng; use serde::de::DeserializeOwned; use serde::Serialize; @@ -13,11 +13,6 @@ use serde::Serialize; use crate::field::extension_field::Frobenius; use crate::util::bits_u64; -pub enum FieldOrder { - U64(u64), - Big(BigUInt) - } - /// A finite field with prime order less than 2^64. pub trait Field: 'static @@ -50,7 +45,7 @@ pub trait Field: const NEG_ONE: Self; const CHARACTERISTIC: u64; - const ORDER: FieldOrder; + const ORDER: BigUint; const TWO_ADICITY: usize; /// Generator of the entire multiplicative group, i.e. all non-zero elements. @@ -218,18 +213,33 @@ pub trait Field: self.exp(power as u64) } + fn exp_bigint(&self, power: BigUint) -> Self { + let digits = power.to_u32_digits(); + let radix = 1u64 << 32; + + let mut result = Self::ONE; + for (radix_power, &digit) in digits.iter().enumerate() { + let mut current = self.exp_u32(digit); + for _ in 0..radix_power { + current = current.exp(radix); + } + result *= current; + } + result + } + /// Returns whether `x^power` is a permutation of this field. fn is_monomial_permutation(power: u64) -> bool { match power { 0 => false, 1 => true, - _ => (Self::ORDER - 1).gcd(&power) == 1, + _ => (Self::ORDER - 1u32).gcd(&BigUint::from(power)) == BigUint::from(1u32), } } fn kth_root(&self, k: u64) -> Self { let p = Self::ORDER; - let p_minus_1 = p - 1; + let p_minus_1 = p - 1u32; debug_assert!( Self::is_monomial_permutation(k), "Not a permutation of this field" @@ -242,10 +252,10 @@ pub trait Field: // x^((p + n(p - 1))/k)^k = x, // implying that x^((p + n(p - 1))/k) is a k'th root of x. for n in 0..k { - let numerator = p as u128 + n as u128 * p_minus_1 as u128; - if numerator % k as u128 == 0 { - let power = (numerator / k as u128) as u64 % p_minus_1; - return self.exp(power); + let numerator = p + p_minus_1 * n; + if numerator % k == BigUint::zero() { + let power = (numerator / k) % p_minus_1; + return self.exp_bigint(power); } } panic!( diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index 1f5bff6f..e4870b37 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -1,3 +1,5 @@ +use num_bigint::BigUint; + use crate::field::field::Field; use crate::util::{bits_u64, ceil_div_usize}; @@ -5,9 +7,9 @@ use crate::util::{bits_u64, ceil_div_usize}; /// `modulus` which cover a range of values and which will /// generate lots of carries, especially at `word_bits` word /// boundaries. -pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec { +pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec { assert!(word_bits == 32 || word_bits == 64); - let modwords = ceil_div_usize(bits_u64(modulus), word_bits); + let modwords = ceil_div_usize(modulus.bits(), word_bits); // Start with basic set close to zero: 0 .. 10 const BIGGEST_SMALL: u32 = 10; let smalls: Vec<_> = (0..BIGGEST_SMALL).map(u64::from).collect(); @@ -32,17 +34,17 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec { let diff_max = basic_inputs .iter() .map(|&x| u64::MAX - x) - .filter(|&x| x < modulus) + .filter(|&x| BigUint::from(x) < modulus) .collect(); // Inputs 'difference from' modulus value let diff_mod = basic_inputs .iter() - .filter(|&&x| x < modulus && x != 0) + .filter(|&&x| BigUint::from(x) < modulus && x != 0) .map(|&x| modulus - x) .collect(); let basics = basic_inputs .into_iter() - .filter(|&x| x < modulus) + .filter(|&x| BigUint::from(x) < modulus) .collect::>(); [basics, diff_max, diff_mod].concat() @@ -59,7 +61,7 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec { /// coordinate-wise to the inputs from `test_inputs(modulus, /// word_bits)` and panic if the two resulting vectors differ. pub fn run_unaryop_test_cases( - modulus: u64, + modulus: BigUint, word_bits: usize, op: UnaryOp, expected_op: ExpectedOp, @@ -90,7 +92,7 @@ pub fn run_unaryop_test_cases( /// `inputs.len()`. Panic if the two functions ever give /// different answers. pub fn run_binaryop_test_cases( - modulus: u64, + modulus: BigUint, word_bits: usize, op: BinaryOp, expected_op: ExpectedOp, diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 89e75875..87fe3db5 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -44,21 +44,3 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { 1.0 / (2.0 * EPSILON * rate.sqrt()) } } - -fn fri_soundness( - num_functions: usize, - rate_bits: usize, - codeword_size_bits: usize, - m: usize, - arity_bits: usize, - num_rounds: usize, - field_size_bits: usize, - num_queries: usize, -) { - let rho = 1.0 / ((1 >> rate_bits) as f32); - - let alpha = rho.sqrt() * (1.0 + 1.0 / (2.0 * m as f32)); - - let term_1 = (m as f32 + 0.5).powi(7) / (rho.powf(1.5) * (1 << (field_size_bits - 2 * codeword_size_bits + 1)) as f32); - let term_2 = (2.0 * m + 1.0) * ((1 << codeword_size_bits) as f32 + 1.0) -} diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 1c642d17..76e7739c 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -115,7 +115,7 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { ) .to_canonical_u64() .leading_zeros() - >= config.proof_of_work_bits + F::ORDER.leading_zeros() + >= config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32 }) .map(F::from_canonical_u64) .expect("Proof of work failed. This is highly unlikely!") diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index b8a7c20f..eda618cd 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -59,7 +59,7 @@ impl, const D: usize> CircuitBuilder { inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros()); + self.assert_leading_zeros(hash, config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32); } pub fn verify_fri_proof( diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index df0c33fc..796c2694 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -58,7 +58,7 @@ fn fri_verify_proof_of_work, const D: usize>( ); ensure!( hash.to_canonical_u64().leading_zeros() - >= config.proof_of_work_bits + F::ORDER.leading_zeros(), + >= config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32, "Invalid proof of work witness." );