diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 2e12ce0f..1cccc68c 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -275,7 +275,7 @@ impl, const D: usize> CircuitBuilder { self.constant(F::TWO) } - /// Returns a routable target with a value of `ORDER - 1`. + /// Returns a routable target with a value of `order() - 1`. pub fn neg_one(&mut self) -> Target { self.constant(F::NEG_ONE) } diff --git a/src/field/cosets.rs b/src/field/cosets.rs index 13ef2e21..f2edd892 100644 --- a/src/field/cosets.rs +++ b/src/field/cosets.rs @@ -1,12 +1,14 @@ +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 { // From Lagrange's theorem. - let num_cosets = (F::ORDER - 1) / (subgroup_size as u64); + let num_cosets = (F::order() - 1u32) / (subgroup_size as u32); assert!( - num_shifts as u64 <= num_cosets, + BigUint::from(num_shifts) <= num_cosets, "The subgroup does not have enough distinct cosets" ); diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 9d38ff1a..051b0fd0 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -4,7 +4,10 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use itertools::Itertools; +use num::bigint::BigUint; use num::Integer; +use rand::Rng; use serde::{Deserialize, Serialize}; use crate::field::extension_field::quadratic::QuadraticCrandallField; @@ -12,6 +15,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,15 +147,18 @@ 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 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); + fn order() -> BigUint { + BigUint::from(FIELD_ORDER) + } + #[inline] fn square(&self) -> Self { *self * *self @@ -170,7 +178,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 +236,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 } @@ -239,6 +247,14 @@ impl Field for CrandallField { Self(n) } + fn to_canonical_biguint(&self) -> BigUint { + BigUint::from(self.to_canonical_u64()) + } + + fn from_canonical_biguint(n: BigUint) -> Self { + Self(n.iter_u64_digits().next().unwrap_or(0)) + } + fn cube_root(&self) -> Self { let x0 = *self; let x1 = x0.square(); @@ -326,6 +342,10 @@ impl Field for CrandallField { } result } + + fn rand_from_rng(rng: &mut R) -> Self { + Self::from_canonical_u64(rng.gen_range(0, FIELD_ORDER)) + } } impl Neg for CrandallField { @@ -336,7 +356,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 +368,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 +391,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) } } @@ -452,7 +472,8 @@ impl Frobenius<1> for CrandallField {} #[cfg(test)] mod tests { - use crate::test_arithmetic; + use crate::{test_field_arithmetic, test_prime_field_arithmetic}; - test_arithmetic!(crate::field::crandall_field::CrandallField); + test_prime_field_arithmetic!(crate::field::crandall_field::CrandallField); + test_field_arithmetic!(crate::field::crandall_field::CrandallField); } diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 2a176fe9..7d706237 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -34,8 +34,8 @@ pub trait Frobenius: OEF { return self.repeated_frobenius(count % D); } let arr = self.to_basefield_array(); - let k = (Self::BaseField::ORDER - 1) / (D as u64); - let z0 = Self::W.exp(k * count as u64); + let k = (Self::BaseField::order() - 1u32) / (D as u64); + let z0 = Self::W.exp_biguint(&(k * count as u64)); let mut res = [Self::BaseField::ZERO; D]; for (i, z) in z0.powers().take(D).enumerate() { res[i] = arr[i] * z; diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 256803ab..5324ad2a 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.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 itertools::Itertools; +use num::bigint::BigUint; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -51,9 +53,7 @@ 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 TWO_ADICITY: usize = 29; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(6483724566312148654), @@ -65,6 +65,10 @@ impl Field for QuadraticCrandallField { const POWER_OF_TWO_GENERATOR: Self = Self([CrandallField::ZERO, CrandallField(14420468973723774561)]); + fn order() -> BigUint { + CrandallField::order() * CrandallField::order() + } + // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { if self.is_zero() { @@ -86,6 +90,24 @@ impl Field for QuadraticCrandallField { >::BaseField::from_canonical_u64(n).into() } + fn to_canonical_biguint(&self) -> BigUint { + let first = self.0[0].to_canonical_biguint(); + let second = self.0[1].to_canonical_biguint(); + let combined = second * Self::CHARACTERISTIC + first; + + combined + } + + fn from_canonical_biguint(n: BigUint) -> Self { + let smaller = n.clone() % Self::CHARACTERISTIC; + let larger = n.clone() / Self::CHARACTERISTIC; + + Self([ + >::BaseField::from_canonical_biguint(smaller), + >::BaseField::from_canonical_biguint(larger), + ]) + } + fn rand_from_rng(rng: &mut R) -> Self { Self([ >::BaseField::rand_from_rng(rng), @@ -200,6 +222,7 @@ mod tests { use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; + use crate::test_field_arithmetic; #[test] fn test_add_neg_sub_mul() { @@ -238,14 +261,14 @@ mod tests { type F = QuadraticCrandallField; let x = F::rand(); assert_eq!( - x.exp(>::BaseField::ORDER), + x.exp_biguint(&>::BaseField::order()), x.frobenius() ); } #[test] fn test_field_order() { - // F::ORDER = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1 + // F::order() = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1 type F = QuadraticCrandallField; let x = F::rand(); assert_eq!( @@ -257,7 +280,7 @@ mod tests { #[test] fn test_power_of_two_gen() { type F = QuadraticCrandallField; - // F::ORDER = 2^29 * 2762315674048163 * 229454332791453 + 1 + // F::order() = 2^29 * 2762315674048163 * 229454332791453 + 1 assert_eq!( F::MULTIPLICATIVE_GROUP_GENERATOR .exp(2762315674048163) @@ -270,4 +293,6 @@ mod tests { >::BaseField::POWER_OF_TWO_GENERATOR.into() ); } + + test_field_arithmetic!(crate::field::extension_field::quadratic::QuadraticCrandallField); } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 9390c521..f38f103a 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -3,6 +3,9 @@ use std::hash::Hash; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use itertools::Itertools; +use num::bigint::BigUint; +use num::traits::Pow; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -73,9 +76,8 @@ 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 TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(12476589904174392631), @@ -93,6 +95,10 @@ impl Field for QuarticCrandallField { CrandallField(15170983443234254033), ]); + fn order() -> BigUint { + CrandallField::order().pow(4u32) + } + // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { if self.is_zero() { @@ -117,6 +123,40 @@ impl Field for QuarticCrandallField { >::BaseField::from_canonical_u64(n).into() } + fn to_canonical_biguint(&self) -> BigUint { + let first = self.0[0].to_canonical_biguint(); + let second = self.0[1].to_canonical_biguint(); + let third = self.0[2].to_canonical_biguint(); + let fourth = self.0[3].to_canonical_biguint(); + + let mut combined = fourth; + combined *= Self::CHARACTERISTIC; + combined += third; + combined *= Self::CHARACTERISTIC; + combined += second; + combined *= Self::CHARACTERISTIC; + combined += first; + + combined + } + + fn from_canonical_biguint(n: BigUint) -> Self { + let first = &n % Self::CHARACTERISTIC; + let mut remaining = &n / Self::CHARACTERISTIC; + let second = &remaining % Self::CHARACTERISTIC; + remaining = remaining / Self::CHARACTERISTIC; + let third = &remaining % Self::CHARACTERISTIC; + remaining = remaining / Self::CHARACTERISTIC; + let fourth = &remaining % Self::CHARACTERISTIC; + + Self([ + >::BaseField::from_canonical_biguint(first), + >::BaseField::from_canonical_biguint(second), + >::BaseField::from_canonical_biguint(third), + >::BaseField::from_canonical_biguint(fourth), + ]) + } + fn rand_from_rng(rng: &mut R) -> Self { Self([ >::BaseField::rand_from_rng(rng), @@ -249,6 +289,7 @@ mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; + use crate::test_field_arithmetic; fn exp_naive(x: F, power: u128) -> F { let mut current = x; @@ -301,7 +342,7 @@ mod tests { const D: usize = 4; let x = F::rand(); assert_eq!( - exp_naive(x, >::BaseField::ORDER as u128), + x.exp_biguint(&>::BaseField::order()), x.frobenius() ); for count in 2..D { @@ -314,7 +355,7 @@ mod tests { #[test] fn test_field_order() { - // F::ORDER = 340282366831806780677557380898690695168 * 340282366831806780677557380898690695170 + 1 + // F::order() = 340282366831806780677557380898690695168 * 340282366831806780677557380898690695170 + 1 type F = QuarticCrandallField; let x = F::rand(); assert_eq!( @@ -329,7 +370,7 @@ mod tests { #[test] fn test_power_of_two_gen() { type F = QuarticCrandallField; - // F::ORDER = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1 + // F::order() = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1 assert_eq!( exp_naive( exp_naive( @@ -346,4 +387,6 @@ mod tests { >::BaseField::POWER_OF_TWO_GENERATOR.into() ); } + + test_field_arithmetic!(crate::field::extension_field::quartic::QuarticCrandallField); } diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 455ee38f..6083e2c4 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -31,8 +31,8 @@ impl ExtensionTarget { return self.repeated_frobenius(count % D, builder); } let arr = self.to_target_array(); - let k = (F::ORDER - 1) / (D as u64); - let z0 = F::Extension::W.exp(k * count as u64); + let k = (F::order() - 1u32) / (D as u64); + let z0 = F::Extension::W.exp_biguint(&(k * count as u64)); let zs = z0 .powers() .take(D) diff --git a/src/field/field.rs b/src/field/field.rs index 28a52202..fd5f8ac1 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -4,7 +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::Integer; +use num::bigint::BigUint; +use num::{Integer, One, Zero}; use rand::Rng; use serde::de::DeserializeOwned; use serde::Serialize; @@ -44,7 +45,6 @@ pub trait Field: const NEG_ONE: Self; const CHARACTERISTIC: u64; - const ORDER: u64; const TWO_ADICITY: usize; /// Generator of the entire multiplicative group, i.e. all non-zero elements. @@ -52,6 +52,8 @@ pub trait Field: /// Generator of a multiplicative subgroup of order `2^TWO_ADICITY`. const POWER_OF_TWO_GENERATOR: Self; + fn order() -> BigUint; + fn is_zero(&self) -> bool { *self == Self::ZERO } @@ -183,6 +185,12 @@ pub trait Field: Self::from_canonical_u64(n as u64) } + fn to_canonical_biguint(&self) -> BigUint; + + fn from_canonical_biguint(n: BigUint) -> Self; + + fn rand_from_rng(rng: &mut R) -> Self; + fn bits(&self) -> usize { bits_u64(self.to_canonical_u64()) } @@ -212,18 +220,33 @@ pub trait Field: self.exp(power as u64) } + fn exp_biguint(&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)).is_one(), } } fn kth_root(&self, k: u64) -> Self { - let p = Self::ORDER; - let p_minus_1 = p - 1; + let p = Self::order().clone(); + let p_minus_1 = &p - 1u32; debug_assert!( Self::is_monomial_permutation(k), "Not a permutation of this field" @@ -236,10 +259,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).is_zero() { + let power = (numerator / k) % p_minus_1; + return self.exp_biguint(&power); } } panic!( @@ -292,10 +315,6 @@ pub trait Field: Self::mds(vec.to_vec()).try_into().unwrap() } - fn rand_from_rng(rng: &mut R) -> Self { - Self::from_canonical_u64(rng.gen_range(0, Self::ORDER)) - } - fn rand() -> Self { Self::rand_from_rng(&mut rand::thread_rng()) } diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index 1f5bff6f..ffab1d9d 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -1,18 +1,21 @@ +use num::{bigint::BigUint, Zero}; + use crate::field::field::Field; -use crate::util::{bits_u64, ceil_div_usize}; +use crate::util::ceil_div_usize; /// Generates a series of non-negative integers less than /// `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 { - assert!(word_bits == 32 || word_bits == 64); - let modwords = ceil_div_usize(bits_u64(modulus), word_bits); +pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec { + //assert!(word_bits == 32 || word_bits == 64); + let modwords = ceil_div_usize(modulus.bits() as usize, 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(); + let smalls: Vec<_> = (0..BIGGEST_SMALL).map(BigUint::from).collect(); // ... and close to MAX: MAX - x - let word_max = (1u64 << word_bits) - 1; + let word_max = (BigUint::from(1u32) << word_bits) - 1u32; + let multiple_words_max = (BigUint::from(1u32) << modwords * word_bits) - 1u32; let bigs = smalls.iter().map(|x| &word_max - x).collect(); let one_words = [smalls, bigs].concat(); // For each of the one word inputs above, create a new one at word i. @@ -22,28 +25,28 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec { one_words .iter() .map(|x| x << (word_bits * i)) - .collect::>() + .collect::>() }) .collect(); - let basic_inputs: Vec = [one_words, multiple_words].concat(); + let basic_inputs: Vec = [one_words, multiple_words].concat(); // Biggest value that will fit in `modwords` words // Inputs 'difference from' maximum value let diff_max = basic_inputs .iter() - .map(|&x| u64::MAX - x) - .filter(|&x| x < modulus) + .map(|x| &multiple_words_max - x) + .filter(|x| x < &modulus) .collect(); // Inputs 'difference from' modulus value let diff_mod = basic_inputs .iter() - .filter(|&&x| x < modulus && x != 0) - .map(|&x| modulus - x) + .filter(|&x| x < &modulus && !x.is_zero()) + .map(|x| &modulus - x) .collect(); let basics = basic_inputs .into_iter() - .filter(|&x| x < modulus) - .collect::>(); + .filter(|x| x < &modulus) + .collect::>(); [basics, diff_max, diff_mod].concat() // // There should be a nicer way to express the code above; something @@ -59,20 +62,21 @@ 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, ) where F: Field, UnaryOp: Fn(F) -> F, - ExpectedOp: Fn(u64) -> u64, + ExpectedOp: Fn(BigUint) -> BigUint, { let inputs = test_inputs(modulus, word_bits); - let expected: Vec<_> = inputs.iter().map(|&x| expected_op(x)).collect(); + let expected: Vec<_> = inputs.iter().map(|x| expected_op(x.clone())).collect(); let output: Vec<_> = inputs .iter() - .map(|&x| op(F::from_canonical_u64(x)).to_canonical_u64()) + .cloned() + .map(|x| op(F::from_canonical_biguint(x)).to_canonical_biguint()) .collect(); // Compare expected outputs with actual outputs for i in 0..inputs.len() { @@ -90,14 +94,14 @@ 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, ) where F: Field, BinaryOp: Fn(F, F) -> F, - ExpectedOp: Fn(u64, u64) -> u64, + ExpectedOp: Fn(BigUint, BigUint) -> BigUint, { let inputs = test_inputs(modulus, word_bits); @@ -122,8 +126,12 @@ pub fn run_binaryop_test_cases( let output: Vec<_> = inputs .iter() .zip(shifted_inputs.clone()) - .map(|(&x, &y)| { - op(F::from_canonical_u64(x), F::from_canonical_u64(y)).to_canonical_u64() + .map(|(x, y)| { + op( + F::from_canonical_biguint(x.clone()), + F::from_canonical_biguint(y.clone()), + ) + .to_canonical_biguint() }) .collect(); @@ -139,108 +147,14 @@ pub fn run_binaryop_test_cases( } #[macro_export] -macro_rules! test_arithmetic { +macro_rules! test_field_arithmetic { ($field:ty) => { - mod arithmetic { - use std::ops::{Add, Mul, Neg, Sub}; + mod field_arithmetic { + use num::{bigint::BigUint, One, Zero}; + use rand::{thread_rng, Rng}; use crate::field::field::Field; - // Can be 32 or 64; doesn't have to be computer's actual word - // bits. Choosing 32 gives more tests... - const WORD_BITS: usize = 32; - - #[test] - fn arithmetic_addition() { - let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases( - modulus, - WORD_BITS, - <$field>::add, - |x, y| { - let (z, over) = x.overflowing_add(y); - if over { - z.overflowing_sub(modulus).0 - } else if z >= modulus { - z - modulus - } else { - z - } - }, - ) - } - - #[test] - fn arithmetic_subtraction() { - let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases( - modulus, - WORD_BITS, - <$field>::sub, - |x, y| { - if x >= y { - x - y - } else { - &modulus - y + x - } - }, - ) - } - - #[test] - fn arithmetic_negation() { - let modulus = <$field>::ORDER; - crate::field::field_testing::run_unaryop_test_cases( - modulus, - WORD_BITS, - <$field>::neg, - |x| { - if x == 0 { - 0 - } else { - modulus - x - } - }, - ) - } - - #[test] - fn arithmetic_multiplication() { - let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases( - modulus, - WORD_BITS, - <$field>::mul, - |x, y| ((x as u128) * (y as u128) % (modulus as u128)) as u64, - ) - } - - #[test] - fn arithmetic_square() { - let modulus = <$field>::ORDER; - crate::field::field_testing::run_unaryop_test_cases( - modulus, - WORD_BITS, - |x: $field| x.square(), - |x| ((x as u128) * (x as u128) % (modulus as u128)) as u64, - ) - } - - #[test] - fn inversion() { - let zero = <$field>::ZERO; - let one = <$field>::ONE; - let order = <$field>::ORDER; - - assert_eq!(zero.try_inverse(), None); - - for &x in &[1, 2, 3, order - 3, order - 2, order - 1] { - let x = <$field>::from_canonical_u64(x); - let inv = x.inverse(); - assert_eq!(x * inv, one); - } - } - #[test] fn batch_inversion() { let xs = (1..=3) @@ -264,10 +178,16 @@ macro_rules! test_arithmetic { #[test] fn negation() { let zero = <$field>::ZERO; - let order = <$field>::ORDER; + let order = <$field>::order(); - for &i in &[0, 1, 2, order - 2, order - 1] { - let i_f = <$field>::from_canonical_u64(i); + for i in [ + BigUint::zero(), + BigUint::one(), + BigUint::from(2u32), + &order - 1u32, + &order - 2u32, + ] { + let i_f = <$field>::from_canonical_biguint(i); assert_eq!(i_f + -i_f, zero); } } @@ -307,13 +227,20 @@ macro_rules! test_arithmetic { } #[test] - fn subtraction() { + fn exponentiation_large() { type F = $field; - let (a, b) = (F::from_canonical_u64((F::ORDER + 1) / 2), F::TWO); - let x = a * b; - assert_eq!(x, F::ONE); - assert_eq!(F::ZERO - x, F::NEG_ONE); + let mut rng = rand::thread_rng(); + + let base = F::rand(); + let pow = BigUint::from(rng.gen::()); + let cycles = rng.gen::(); + let mul_group_order = F::order() - 1u32; + let big_pow = &pow + &mul_group_order * cycles; + let big_pow_wrong = &pow + &mul_group_order * cycles + 1u32; + + assert_eq!(base.exp_biguint(&pow), base.exp_biguint(&big_pow)); + assert_ne!(base.exp_biguint(&pow), base.exp_biguint(&big_pow_wrong)); } #[test] @@ -332,3 +259,122 @@ macro_rules! test_arithmetic { } }; } + +#[macro_export] +macro_rules! test_prime_field_arithmetic { + ($field:ty) => { + mod prime_field_arithmetic { + use std::ops::{Add, Mul, Neg, Sub}; + + use num::{bigint::BigUint, One, Zero}; + + use crate::field::field::Field; + + // Can be 32 or 64; doesn't have to be computer's actual word + // bits. Choosing 32 gives more tests... + const WORD_BITS: usize = 32; + + #[test] + fn arithmetic_addition() { + let modulus = <$field>::order(); + crate::field::field_testing::run_binaryop_test_cases( + modulus.clone(), + WORD_BITS, + <$field>::add, + |x, y| (&x + &y) % &modulus, + ) + } + + #[test] + fn arithmetic_subtraction() { + let modulus = <$field>::order(); + crate::field::field_testing::run_binaryop_test_cases( + modulus.clone(), + WORD_BITS, + <$field>::sub, + |x, y| { + if x >= y { + &x - &y + } else { + &modulus - &y + &x + } + }, + ) + } + + #[test] + fn arithmetic_negation() { + let modulus = <$field>::order(); + crate::field::field_testing::run_unaryop_test_cases( + modulus.clone(), + WORD_BITS, + <$field>::neg, + |x| { + if x.is_zero() { + BigUint::zero() + } else { + &modulus - &x + } + }, + ) + } + + #[test] + fn arithmetic_multiplication() { + let modulus = <$field>::order(); + crate::field::field_testing::run_binaryop_test_cases( + modulus.clone(), + WORD_BITS, + <$field>::mul, + |x, y| &x * &y % &modulus, + ) + } + + #[test] + fn arithmetic_square() { + let modulus = <$field>::order(); + crate::field::field_testing::run_unaryop_test_cases( + modulus.clone(), + WORD_BITS, + |x: $field| x.square(), + |x| (&x * &x) % &modulus, + ) + } + + #[test] + fn inversion() { + let zero = <$field>::ZERO; + let one = <$field>::ONE; + let order = <$field>::order(); + + assert_eq!(zero.try_inverse(), None); + + for x in [ + BigUint::one(), + BigUint::from(2u32), + BigUint::from(3u32), + &order - 3u32, + &order - 2u32, + &order - 1u32, + ] { + let x = <$field>::from_canonical_biguint(x); + let inv = x.inverse(); + assert_eq!(x * inv, one); + } + } + + #[test] + fn subtraction() { + type F = $field; + + let (a, b) = ( + F::from_canonical_biguint((F::order() + 1u32) / 2u32), + F::TWO, + ); + let x = a * b; + assert_eq!(x, F::ONE); + assert_eq!(F::ZERO - x, F::NEG_ONE); + } + } + }; +} diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 5a8f09e5..a4147da7 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -111,7 +111,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 588ad423..2327418f 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -65,7 +65,10 @@ 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 6803c00d..27e775e6 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -59,7 +59,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." );