From 1dd850b0e546162f58e978c665f1ced62ee1a5f3 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 21 Jul 2021 13:05:32 -0700 Subject: [PATCH] fixes --- src/circuit_builder.rs | 2 +- src/field/cosets.rs | 2 +- src/field/crandall_field.rs | 12 +++- src/field/extension_field/mod.rs | 4 +- src/field/extension_field/quadratic.rs | 23 ++++++-- src/field/extension_field/quartic.rs | 27 +++++++-- src/field/extension_field/target.rs | 4 +- src/field/field.rs | 19 +++--- src/field/field_testing.rs | 82 ++++++++++---------------- src/fri/prover.rs | 2 +- src/fri/recursive_verifier.rs | 2 +- src/fri/verifier.rs | 2 +- 12 files changed, 104 insertions(+), 77 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 2cfeb720..ad9d9337 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -274,7 +274,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 0f43a12b..d8dd27b4 100644 --- a/src/field/cosets.rs +++ b/src/field/cosets.rs @@ -5,7 +5,7 @@ use crate::field::field::Field; /// `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 - 1u32) / (subgroup_size as u32); + 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" diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 465b0593..ce5a2b3a 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 itertools::Itertools; use num_bigint::BigUint; use num::Integer; use rand::Rng; @@ -148,13 +149,16 @@ impl Field for CrandallField { const TWO: Self = Self(2); const NEG_ONE: Self = Self(FIELD_ORDER - 1); - const ORDER: BigUint = BigUint::from(FIELD_ORDER); const TWO_ADICITY: usize = 28; 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 @@ -243,6 +247,12 @@ impl Field for CrandallField { Self(n) } + fn from_canonical_biguint(n: BigUint) -> Self { + let last_two : Vec<_> = n.to_u32_digits().iter().rev().take(2).pad_using(2, |_| &0u32).map(|x| *x as u64).collect(); + let n_u64 = last_two[0] + (1u64 << 32) * last_two[1]; + Self(n_u64) + } + fn cube_root(&self) -> Self { let x0 = *self; let x1 = x0.square(); diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index f579004f..6a927b04 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 - 1u32) / (D as u64); - let z0 = Self::W.exp_bigint(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 9ed3a1ee..4c734188 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 itertools::Itertools; use num_bigint::BigUint; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -53,7 +54,6 @@ impl Field for QuadraticCrandallField { const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]); 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), @@ -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,17 @@ impl Field for QuadraticCrandallField { >::BaseField::from_canonical_u64(n).into() } + fn from_canonical_biguint(n: BigUint) -> Self { + let last_four : Vec<_> = n.to_u32_digits().iter().rev().take(4).pad_using(4, |_| &0u32).map(|x| *x as u64).collect(); + let last_u64 = last_four[0] + (1u64 << 32) * last_four[1]; + let next_last_u64 = last_four[2] + (1u64 << 32) * last_four[3]; + + Self([ + >::BaseField::from_canonical_u64(last_u64), + >::BaseField::from_canonical_u64(next_last_u64), + ]) + } + fn rand_from_rng(rng: &mut R) -> Self { Self([ >::BaseField::rand_from_rng(rng), @@ -238,14 +253,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 +272,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) diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 7539b272..8eb0d6fb 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.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 itertools::Itertools; use num::traits::Pow; use num_bigint::BigUint; use rand::Rng; @@ -77,7 +78,6 @@ impl Field for QuarticCrandallField { const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC; // Does not fit in 64-bits. - const ORDER: BigUint = CrandallField::ORDER.pow(4u32); const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(12476589904174392631), @@ -95,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() { @@ -119,6 +123,21 @@ impl Field for QuarticCrandallField { >::BaseField::from_canonical_u64(n).into() } + fn from_canonical_biguint(n: BigUint) -> Self { + let last_eight : Vec<_> = n.to_u32_digits().iter().rev().take(8).pad_using(8, |_| &0u32).map(|x| *x as u64).collect(); + let last_u64 = last_eight[0] + (1u64 << 32) * last_eight[1]; + let next_last_u64 = last_eight[2] + (1u64 << 32) * last_eight[3]; + let third_last_u64 = last_eight[4] + (1u64 << 32) * last_eight[5]; + let fourth_last_u64 = last_eight[6] + (1u64 << 32) * last_eight[7]; + + Self([ + >::BaseField::from_canonical_u64(last_u64), + >::BaseField::from_canonical_u64(next_last_u64), + >::BaseField::from_canonical_u64(third_last_u64), + >::BaseField::from_canonical_u64(fourth_last_u64), + ]) + } + fn rand_from_rng(rng: &mut R) -> Self { Self([ >::BaseField::rand_from_rng(rng), @@ -303,7 +322,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 { @@ -316,7 +335,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!( @@ -331,7 +350,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( diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index f13255b4..502ea896 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 - 1u32) / (D as u64); - let z0 = F::Extension::W.exp_bigint(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 bcdafc61..ce4e52be 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -45,7 +45,6 @@ pub trait Field: const NEG_ONE: Self; const CHARACTERISTIC: u64; - const ORDER: BigUint; const TWO_ADICITY: usize; /// Generator of the entire multiplicative group, i.e. all non-zero elements. @@ -53,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 } @@ -184,6 +185,8 @@ pub trait Field: Self::from_canonical_u64(n as u64) } + fn from_canonical_biguint(n: BigUint) -> Self; + fn rand_from_rng(rng: &mut R) -> Self; fn bits(&self) -> usize { @@ -215,7 +218,7 @@ pub trait Field: self.exp(power as u64) } - fn exp_bigint(&self, power: BigUint) -> Self { + fn exp_biguint(&self, power: BigUint) -> Self { let digits = power.to_u32_digits(); let radix = 1u64 << 32; @@ -235,13 +238,13 @@ pub trait Field: match power { 0 => false, 1 => true, - _ => (Self::ORDER - 1u32).gcd(&BigUint::from(power)) == BigUint::from(1u32), + _ => (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 - 1u32; + let p = Self::order().clone(); + let p_minus_1 = p.clone() - 1u32; debug_assert!( Self::is_monomial_permutation(k), "Not a permutation of this field" @@ -254,10 +257,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 + p_minus_1 * n; - if numerator % k == BigUint::zero() { + let numerator = p.clone() + &p_minus_1 * n; + if numerator.clone() % k == BigUint::zero() { let power = (numerator / k) % p_minus_1; - return self.exp_bigint(power); + return self.exp_biguint(power); } } panic!( diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index e4870b37..c30c641e 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -7,14 +7,14 @@ 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: BigUint, word_bits: usize) -> Vec { - assert!(word_bits == 32 || word_bits == 64); +pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec { + //assert!(word_bits == 32 || word_bits == 64); 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(); + 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 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. @@ -24,28 +24,28 @@ pub fn test_inputs(modulus: BigUint, 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) + .map(|&x| word_max - x) .filter(|&x| BigUint::from(x) < modulus) .collect(); // Inputs 'difference from' modulus value let diff_mod = basic_inputs .iter() - .filter(|&&x| BigUint::from(x) < modulus && x != 0) + .filter(|&&x| BigUint::from(x) < modulus && x != BigUint::from(0u32)) .map(|&x| modulus - x) .collect(); let basics = basic_inputs .into_iter() .filter(|&x| BigUint::from(x) < modulus) - .collect::>(); + .collect::>(); [basics, diff_max, diff_mod].concat() // // There should be a nicer way to express the code above; something @@ -68,13 +68,13 @@ pub fn run_unaryop_test_cases( ) 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 output: Vec<_> = inputs .iter() - .map(|&x| op(F::from_canonical_u64(x)).to_canonical_u64()) + .map(|&x| op(F::from_canonical_biguint(x)).to_canonical_biguint()) .collect(); // Compare expected outputs with actual outputs for i in 0..inputs.len() { @@ -99,7 +99,7 @@ pub fn run_binaryop_test_cases( ) 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); @@ -125,7 +125,7 @@ pub fn run_binaryop_test_cases( .iter() .zip(shifted_inputs.clone()) .map(|(&x, &y)| { - op(F::from_canonical_u64(x), F::from_canonical_u64(y)).to_canonical_u64() + op(F::from_canonical_biguint(x), F::from_canonical_biguint(y)).to_canonical_u64() }) .collect(); @@ -145,6 +145,7 @@ macro_rules! test_arithmetic { ($field:ty) => { mod arithmetic { use std::ops::{Add, Mul, Neg, Sub}; + use num_bigint::BigUint; use crate::field::field::Field; @@ -154,77 +155,56 @@ macro_rules! test_arithmetic { #[test] fn arithmetic_addition() { - let modulus = <$field>::ORDER; + 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 - } - }, + BigUint::add, ) } #[test] fn arithmetic_subtraction() { - let modulus = <$field>::ORDER; + 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 - } - }, + BigUint::sub, ) } #[test] fn arithmetic_negation() { - let modulus = <$field>::ORDER; + 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 - } - }, + BigUint::neg, ) } #[test] fn arithmetic_multiplication() { - let modulus = <$field>::ORDER; + 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, + BigUint::mul, ) } #[test] fn arithmetic_square() { - let modulus = <$field>::ORDER; + 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, + |x| x * x, ) } @@ -232,12 +212,12 @@ macro_rules! test_arithmetic { fn inversion() { let zero = <$field>::ZERO; let one = <$field>::ONE; - let order = <$field>::ORDER; + 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); + for &x in &[BigUint::from(1u32), 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); } @@ -266,10 +246,10 @@ 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::from(0u32), BigUint::from(1u32), BigUint::from(2u32), order - 2u32, order - 1u32] { + let i_f = <$field>::from_canonical_biguint(i); assert_eq!(i_f + -i_f, zero); } } @@ -312,7 +292,7 @@ macro_rules! test_arithmetic { fn subtraction() { type F = $field; - let (a, b) = (F::from_canonical_u64((F::ORDER + 1) / 2), F::TWO); + 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 76e7739c..b6fdd35b 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 + (64 - F::ORDER.bits()) as u32 + >= 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 e6f8474f..1891c677 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 + (64 - F::ORDER.bits()) as u32); + 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 712288fd..06f065c1 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 + (64 - F::ORDER.bits()) as u32, + >= config.proof_of_work_bits + (64 - F::order().bits()) as u32, "Invalid proof of work witness." );