From a2cf2c03b620e3933a3ce275b9b3aa239fcbb7a0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 15:22:06 +0200 Subject: [PATCH 1/8] Working FRI with field extensions --- src/bin/bench_recursion.rs | 4 +- src/circuit_data.rs | 7 +- src/field/crandall_field.rs | 11 +++ src/field/extension_field/mod.rs | 96 +++++++++++++++++- .../{binary.rs => quadratic.rs} | 93 ++++++++++++------ src/field/extension_field/quartic.rs | 36 ++++++- src/fri/mod.rs | 19 +++- src/fri/prover.rs | 35 ++++--- src/fri/verifier.rs | 72 +++++++++----- src/plonk_challenger.rs | 35 +++++++ src/polynomial/commitment.rs | 97 +++++++++++-------- src/polynomial/polynomial.rs | 8 ++ src/proof.rs | 17 ++-- src/prover.rs | 7 +- 14 files changed, 407 insertions(+), 130 deletions(-) rename src/field/extension_field/{binary.rs => quadratic.rs} (70%) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 255dd985..a9b0aa0c 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -3,11 +3,13 @@ use env_logger::Env; use plonky2::circuit_builder::CircuitBuilder; use plonky2::circuit_data::CircuitConfig; use plonky2::field::crandall_field::CrandallField; +use plonky2::field::extension_field::Extendable; use plonky2::field::field::Field; use plonky2::fri::FriConfig; use plonky2::gates::constant::ConstantGate; use plonky2::gates::gmimc::GMiMCGate; use plonky2::hash::GMIMC_ROUNDS; +use plonky2::polynomial::commitment::EXTENSION_DEGREE; use plonky2::prover::PLONK_BLINDING; use plonky2::witness::PartialWitness; @@ -27,7 +29,7 @@ fn main() { // bench_gmimc::(); } -fn bench_prove() { +fn bench_prove>() { let gmimc_gate = GMiMCGate::::with_automatic_constants(); let config = CircuitConfig { diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 702df2f4..f9bdf420 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -1,9 +1,10 @@ +use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; use crate::merkle_tree::MerkleTree; -use crate::polynomial::commitment::ListPolynomialCommitment; +use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE}; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; use crate::verifier::verify; @@ -55,7 +56,7 @@ pub struct CircuitData { pub(crate) common: CommonCircuitData, } -impl CircuitData { +impl> CircuitData { pub fn prove(&self, inputs: PartialWitness) -> Proof { prove(&self.prover_only, &self.common, inputs) } @@ -77,7 +78,7 @@ pub struct ProverCircuitData { pub(crate) common: CommonCircuitData, } -impl ProverCircuitData { +impl> ProverCircuitData { pub fn prove(&self, inputs: PartialWitness) -> Proof { prove(&self.prover_only, &self.common, inputs) } diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 99028016..261ec0f1 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -4,6 +4,9 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; +use crate::field::extension_field::quadratic::QuadraticCrandallField; +use crate::field::extension_field::quartic::QuarticCrandallField; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; @@ -412,6 +415,14 @@ impl DivAssign for CrandallField { } } +impl Extendable<2> for CrandallField { + type Extension = QuadraticCrandallField; +} + +impl Extendable<4> for CrandallField { + type Extension = QuarticCrandallField; +} + /// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the /// field order and `2^64`. #[inline] diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 3380e792..7d1f1adf 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,2 +1,96 @@ -pub mod binary; +use crate::field::extension_field::quadratic::QuadraticFieldExtension; +use crate::field::extension_field::quartic::QuarticFieldExtension; +use crate::field::field::Field; + +pub mod quadratic; pub mod quartic; + +pub trait Extendable: Sized { + type Extension: Field + FieldExtension + From; +} + +impl Extendable<1> for F { + type Extension = Self; +} + +pub trait FieldExtension: Field { + type BaseField: Field; + + fn to_basefield_array(&self) -> [Self::BaseField; D]; + + fn from_basefield_array(arr: [Self::BaseField; D]) -> Self; + + fn from_basefield(x: Self::BaseField) -> Self; +} + +impl FieldExtension<1> for F { + type BaseField = F; + + fn to_basefield_array(&self) -> [Self::BaseField; 1] { + [*self] + } + + fn from_basefield_array(arr: [Self::BaseField; 1]) -> Self { + arr[0] + } + + fn from_basefield(x: Self::BaseField) -> Self { + x + } +} + +impl FieldExtension<2> for FE { + type BaseField = FE::BaseField; + + fn to_basefield_array(&self) -> [Self::BaseField; 2] { + self.to_canonical_representation() + } + + fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self { + Self::from_canonical_representation(arr) + } + + fn from_basefield(x: Self::BaseField) -> Self { + x.into() + } +} + +impl FieldExtension<4> for FE { + type BaseField = FE::BaseField; + + fn to_basefield_array(&self) -> [Self::BaseField; 4] { + self.to_canonical_representation() + } + + fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self { + Self::from_canonical_representation(arr) + } + + fn from_basefield(x: Self::BaseField) -> Self { + x.into() + } +} + +/// Flatten the slice by sending every extension field element to its D-sized canonical representation. +pub fn flatten(l: &[F::Extension]) -> Vec +where + F: Extendable, +{ + l.iter() + .flat_map(|x| x.to_basefield_array().to_vec()) + .collect() +} + +/// Batch every D-sized chunks into extension field elements. +pub fn unflatten(l: &[F]) -> Vec +where + F: Extendable, +{ + l.chunks_exact(D) + .map(|c| { + let mut arr = [F::ZERO; D]; + arr.copy_from_slice(c); + F::Extension::from_basefield_array(arr) + }) + .collect() +} diff --git a/src/field/extension_field/binary.rs b/src/field/extension_field/quadratic.rs similarity index 70% rename from src/field/extension_field/binary.rs rename to src/field/extension_field/quadratic.rs index 024e005d..a9a9e5b1 100644 --- a/src/field/extension_field/binary.rs +++ b/src/field/extension_field/quadratic.rs @@ -6,7 +6,9 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -pub trait BinaryFieldExtension: Field { +pub trait QuadraticFieldExtension: + Field + From<::BaseField> +{ type BaseField: Field; // Element W of BaseField, such that `X^2 - W` is irreducible over BaseField. @@ -35,9 +37,9 @@ pub trait BinaryFieldExtension: Field { } #[derive(Copy, Clone)] -pub struct BinaryCrandallField([CrandallField; 2]); +pub struct QuadraticCrandallField([CrandallField; 2]); -impl BinaryFieldExtension for BinaryCrandallField { +impl QuadraticFieldExtension for QuadraticCrandallField { type BaseField = CrandallField; // Verifiable in Sage with // ``R. = GF(p)[]; assert (x^2 -3).is_irreducible()`. @@ -57,15 +59,21 @@ impl BinaryFieldExtension for BinaryCrandallField { } } -impl PartialEq for BinaryCrandallField { +impl From<::BaseField> for QuadraticCrandallField { + fn from(x: ::BaseField) -> Self { + Self([x, ::BaseField::ZERO]) + } +} + +impl PartialEq for QuadraticCrandallField { fn eq(&self, other: &Self) -> bool { self.to_canonical_representation() == other.to_canonical_representation() } } -impl Eq for BinaryCrandallField {} +impl Eq for QuadraticCrandallField {} -impl Hash for BinaryCrandallField { +impl Hash for QuadraticCrandallField { fn hash(&self, state: &mut H) { for l in &self.to_canonical_representation() { Hash::hash(l, state); @@ -73,14 +81,14 @@ impl Hash for BinaryCrandallField { } } -impl Field for BinaryCrandallField { +impl Field for QuadraticCrandallField { const ZERO: Self = Self([CrandallField::ZERO; 2]); const ONE: Self = Self([CrandallField::ONE, CrandallField::ZERO]); const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]); const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]); // Does not fit in 64-bits. - const ORDER: u64 = 0; + const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading. const TWO_ADICITY: usize = 29; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([CrandallField(3), CrandallField::ONE]); const POWER_OF_TWO_GENERATOR: Self = @@ -99,38 +107,57 @@ impl Field for BinaryCrandallField { Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) } + // It's important that the primitive roots of unity are the same as the ones in the base field, + // otherwise the FFT doesn't commute with field inclusion. + fn primitive_root_of_unity(n_log: usize) -> Self { + if n_log <= CrandallField::TWO_ADICITY { + Self([ + CrandallField::primitive_root_of_unity(n_log), + CrandallField::ZERO, + ]) + } else { + // The root of unity isn't in the base field so we need to compute it manually. + assert!(n_log <= Self::TWO_ADICITY); + let mut base = Self::POWER_OF_TWO_GENERATOR; + for _ in n_log..Self::TWO_ADICITY { + base = base.square(); + } + base + } + } + fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } fn from_canonical_u64(n: u64) -> Self { Self([ - ::BaseField::from_canonical_u64(n), - ::BaseField::ZERO, + ::BaseField::from_canonical_u64(n), + ::BaseField::ZERO, ]) } fn rand_from_rng(rng: &mut R) -> Self { Self([ - ::BaseField::rand_from_rng(rng), - ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), ]) } } -impl Display for BinaryCrandallField { +impl Display for QuadraticCrandallField { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} + {}*a", self.0[0], self.0[1]) } } -impl Debug for BinaryCrandallField { +impl Debug for QuadraticCrandallField { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } } -impl Neg for BinaryCrandallField { +impl Neg for QuadraticCrandallField { type Output = Self; #[inline] @@ -139,7 +166,7 @@ impl Neg for BinaryCrandallField { } } -impl Add for BinaryCrandallField { +impl Add for QuadraticCrandallField { type Output = Self; #[inline] @@ -148,19 +175,19 @@ impl Add for BinaryCrandallField { } } -impl AddAssign for BinaryCrandallField { +impl AddAssign for QuadraticCrandallField { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } -impl Sum for BinaryCrandallField { +impl Sum for QuadraticCrandallField { fn sum>(iter: I) -> Self { iter.fold(Self::ZERO, |acc, x| acc + x) } } -impl Sub for BinaryCrandallField { +impl Sub for QuadraticCrandallField { type Output = Self; #[inline] @@ -169,14 +196,14 @@ impl Sub for BinaryCrandallField { } } -impl SubAssign for BinaryCrandallField { +impl SubAssign for QuadraticCrandallField { #[inline] fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } -impl Mul for BinaryCrandallField { +impl Mul for QuadraticCrandallField { type Output = Self; #[inline] @@ -191,20 +218,20 @@ impl Mul for BinaryCrandallField { } } -impl MulAssign for BinaryCrandallField { +impl MulAssign for QuadraticCrandallField { #[inline] fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; } } -impl Product for BinaryCrandallField { +impl Product for QuadraticCrandallField { fn product>(iter: I) -> Self { iter.fold(Self::ONE, |acc, x| acc * x) } } -impl Div for BinaryCrandallField { +impl Div for QuadraticCrandallField { type Output = Self; #[allow(clippy::suspicious_arithmetic_impl)] @@ -213,7 +240,7 @@ impl Div for BinaryCrandallField { } } -impl DivAssign for BinaryCrandallField { +impl DivAssign for QuadraticCrandallField { fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; } @@ -221,7 +248,9 @@ impl DivAssign for BinaryCrandallField { #[cfg(test)] mod tests { - use crate::field::extension_field::binary::{BinaryCrandallField, BinaryFieldExtension}; + use crate::field::extension_field::quadratic::{ + QuadraticCrandallField, QuadraticFieldExtension, + }; use crate::field::field::Field; fn exp_naive(x: F, power: u64) -> F { @@ -239,7 +268,7 @@ mod tests { #[test] fn test_add_neg_sub_mul() { - type F = BinaryCrandallField; + type F = QuadraticCrandallField; let x = F::rand(); let y = F::rand(); let z = F::rand(); @@ -247,7 +276,7 @@ mod tests { assert_eq!(-x, F::ZERO - x); assert_eq!( x + x, - x.scalar_mul(::BaseField::TWO) + x.scalar_mul(::BaseField::TWO) ); assert_eq!(x * (-x), -x.square()); assert_eq!(x + y, y + x); @@ -260,7 +289,7 @@ mod tests { #[test] fn test_inv_div() { - type F = BinaryCrandallField; + type F = QuadraticCrandallField; let x = F::rand(); let y = F::rand(); let z = F::rand(); @@ -274,10 +303,10 @@ mod tests { #[test] fn test_frobenius() { - type F = BinaryCrandallField; + type F = QuadraticCrandallField; let x = F::rand(); assert_eq!( - exp_naive(x, ::BaseField::ORDER), + exp_naive(x, ::BaseField::ORDER), x.frobenius() ); } @@ -285,7 +314,7 @@ mod tests { #[test] fn test_field_order() { // F::ORDER = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1 - type F = BinaryCrandallField; + type F = QuadraticCrandallField; let x = F::rand(); assert_eq!( exp_naive(exp_naive(x, 18446744071293632512), 18446744071293632514), diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 3137d88c..d72fbeb8 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -6,7 +6,7 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -pub trait QuarticFieldExtension: Field { +pub trait QuarticFieldExtension: Field + From<::BaseField> { type BaseField: Field; // Element W of BaseField, such that `X^4 - W` is irreducible over BaseField. @@ -65,6 +65,17 @@ impl QuarticFieldExtension for QuarticCrandallField { } } +impl From<::BaseField> for QuarticCrandallField { + fn from(x: ::BaseField) -> Self { + Self([ + x, + ::BaseField::ZERO, + ::BaseField::ZERO, + ::BaseField::ZERO, + ]) + } +} + impl PartialEq for QuarticCrandallField { fn eq(&self, other: &Self) -> bool { self.to_canonical_representation() == other.to_canonical_representation() @@ -103,7 +114,7 @@ impl Field for QuarticCrandallField { ]); // Does not fit in 64-bits. - const ORDER: u64 = 0; + const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading. const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(3), @@ -134,6 +145,27 @@ impl Field for QuarticCrandallField { Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) } + // It's important that the primitive roots of unity are the same as the ones in the base field, + // otherwise the FFT doesn't commute with field inclusion. + fn primitive_root_of_unity(n_log: usize) -> Self { + if n_log <= CrandallField::TWO_ADICITY { + Self([ + CrandallField::primitive_root_of_unity(n_log), + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]) + } else { + // The root of unity isn't in the base field so we need to compute it manually. + assert!(n_log <= Self::TWO_ADICITY); + let mut base = Self::POWER_OF_TWO_GENERATOR; + for _ in n_log..Self::TWO_ADICITY { + base = base.square(); + } + base + } + } + fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 6c8401a2..f00fe8f8 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -52,13 +52,17 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { mod tests { use super::*; use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quadratic::QuadraticCrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::extension_field::{flatten, FieldExtension}; use crate::field::fft::ifft; use crate::field::field::Field; use crate::fri::prover::fri_proof; use crate::fri::verifier::verify_fri_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; - use crate::polynomial::polynomial::PolynomialCoeffs; + use crate::polynomial::commitment::EXTENSION_DEGREE; + use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::reverse_index_bits_in_place; use anyhow::Result; use rand::rngs::ThreadRng; @@ -71,6 +75,7 @@ mod tests { num_query_rounds: usize, ) -> Result<()> { type F = CrandallField; + type F2 = QuadraticCrandallField; let n = 1 << degree_log; let coeffs = PolynomialCoeffs::new(F::rand_vec(n)).lde(rate_bits); @@ -91,15 +96,23 @@ mod tests { reverse_index_bits_in_place(&mut leaves); MerkleTree::new(leaves, false) }; + let coset_lde = + PolynomialValues::new(coset_lde.values.into_iter().map(|x| F2::from(x)).collect()); let root = tree.root; let mut challenger = Challenger::new(); - let proof = fri_proof(&[&tree], &coeffs, &coset_lde, &mut challenger, &config); + let proof = fri_proof( + &[&tree], + &coeffs.to_extension::(), + &coset_lde, + &mut challenger, + &config, + ); let mut challenger = Challenger::new(); verify_fri_proof( degree_log, &[], - F::ONE, + F2::ONE, &[root], &proof, &mut challenger, diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 084ccf08..cfbf40e1 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -1,9 +1,11 @@ +use crate::field::extension_field::{flatten, unflatten, Extendable, FieldExtension}; use crate::field::field::Field; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; +use crate::polynomial::commitment::EXTENSION_DEGREE; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, Hash}; use crate::util::reverse_index_bits_in_place; @@ -12,12 +14,15 @@ use crate::util::reverse_index_bits_in_place; pub fn fri_proof( initial_merkle_trees: &[&MerkleTree], // Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero. - lde_polynomial_coeffs: &PolynomialCoeffs, + lde_polynomial_coeffs: &PolynomialCoeffs, // Evaluation of the polynomial on the large domain. - lde_polynomial_values: &PolynomialValues, + lde_polynomial_values: &PolynomialValues, challenger: &mut Challenger, config: &FriConfig, -) -> FriProof { +) -> FriProof +where + F: Extendable, +{ let n = lde_polynomial_values.values.len(); assert_eq!(lde_polynomial_coeffs.coeffs.len(), n); @@ -46,11 +51,14 @@ pub fn fri_proof( } fn fri_committed_trees( - polynomial_coeffs: &PolynomialCoeffs, - polynomial_values: &PolynomialValues, + polynomial_coeffs: &PolynomialCoeffs, + polynomial_values: &PolynomialValues, challenger: &mut Challenger, config: &FriConfig, -) -> (Vec>, PolynomialCoeffs) { +) -> (Vec>, PolynomialCoeffs) +where + F: Extendable, +{ let mut values = polynomial_values.clone(); let mut coeffs = polynomial_coeffs.clone(); @@ -66,7 +74,7 @@ fn fri_committed_trees( values .values .chunks(arity) - .map(|chunk| chunk.to_vec()) + .map(|chunk: &[F::Extension]| flatten(chunk)) .collect(), false, ); @@ -74,7 +82,7 @@ fn fri_committed_trees( challenger.observe_hash(&tree.root); trees.push(tree); - let beta = challenger.get_challenge(); + let beta = challenger.get_extension_challenge(); // P(x) = sum_{i( ); shift = shift.exp_u32(arity as u32); // TODO: Is it faster to interpolate? - values = coeffs.clone().coset_fft(shift); + values = coeffs.clone().coset_fft(shift.into()) } - challenger.observe_elements(&coeffs.coeffs); + challenger.observe_extension_elements(&coeffs.coeffs); (trees, coeffs) } @@ -112,7 +120,7 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { .expect("Proof of work failed.") } -fn fri_prover_query_rounds( +fn fri_prover_query_rounds>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, @@ -124,7 +132,7 @@ fn fri_prover_query_rounds( .collect() } -fn fri_prover_query_round( +fn fri_prover_query_round>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, @@ -134,6 +142,7 @@ fn fri_prover_query_round( let mut query_steps = Vec::new(); let x = challenger.get_challenge(); let mut x_index = x.to_canonical_u64() as usize % n; + dbg!(x_index, n); let initial_proof = initial_merkle_trees .iter() .map(|t| (t.get(x_index).to_vec(), t.prove(x_index))) @@ -141,7 +150,7 @@ fn fri_prover_query_round( for (i, tree) in trees.iter().enumerate() { let arity_bits = config.reduction_arity_bits[i]; let arity = 1 << arity_bits; - let mut evals = tree.get(x_index >> arity_bits).to_vec(); + let mut evals = unflatten(tree.get(x_index >> arity_bits)); evals.remove(x_index & (arity - 1)); let merkle_proof = tree.prove(x_index >> arity_bits); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 9e2f0164..09819743 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -1,10 +1,11 @@ +use crate::field::extension_field::{flatten, Extendable, FieldExtension}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; -use crate::polynomial::commitment::SALT_SIZE; +use crate::polynomial::commitment::{EXTENSION_DEGREE, SALT_SIZE}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash}; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -16,9 +17,12 @@ fn compute_evaluation( x: F, old_x_index: usize, arity_bits: usize, - last_evals: &[F], - beta: F, -) -> F { + last_evals: &[F::Extension], + beta: F::Extension, +) -> F::Extension +where + F: Extendable, +{ debug_assert_eq!(last_evals.len(), 1 << arity_bits); let g = F::primitive_root_of_unity(arity_bits); @@ -32,8 +36,9 @@ fn compute_evaluation( let points = g .powers() .zip(evals) - .map(|(y, e)| (x * y, e)) + .map(|(y, e)| ((x * y).into(), e)) .collect::>(); + dbg!(&points); let barycentric_weights = barycentric_weights(&points); interpolate(&points, beta, &barycentric_weights) } @@ -42,7 +47,10 @@ fn fri_verify_proof_of_work( proof: &FriProof, challenger: &mut Challenger, config: &FriConfig, -) -> Result<()> { +) -> Result<()> +where + F: Extendable, +{ let hash = hash_n_to_1( challenger .get_hash() @@ -65,14 +73,17 @@ fn fri_verify_proof_of_work( pub fn verify_fri_proof( purported_degree_log: usize, // Point-evaluation pairs for polynomial commitments. - points: &[(F, F)], + points: &[(F::Extension, F::Extension)], // Scaling factor to combine polynomials. - alpha: F, + alpha: F::Extension, initial_merkle_roots: &[Hash], proof: &FriProof, challenger: &mut Challenger, config: &FriConfig, -) -> Result<()> { +) -> Result<()> +where + F: Extendable, +{ let total_arities = config.reduction_arity_bits.iter().sum::(); ensure!( purported_degree_log @@ -89,10 +100,10 @@ pub fn verify_fri_proof( .iter() .map(|root| { challenger.observe_hash(root); - challenger.get_challenge() + challenger.get_extension_challenge() }) .collect::>(); - challenger.observe_elements(&proof.final_poly.coeffs); + challenger.observe_extension_elements(&proof.final_poly.coeffs); // Check PoW. fri_verify_proof_of_work(proof, challenger, config)?; @@ -140,37 +151,46 @@ fn fri_verify_initial_proof( fn fri_combine_initial( proof: &FriInitialTreeProof, - alpha: F, - interpolant: &PolynomialCoeffs, - points: &[(F, F)], + alpha: F::Extension, + interpolant: &PolynomialCoeffs, + points: &[(F::Extension, F::Extension)], subgroup_x: F, config: &FriConfig, -) -> F { +) -> F::Extension +where + F: Extendable, +{ let e = proof .evals_proofs .iter() .enumerate() .flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }]) .rev() - .fold(F::ZERO, |acc, &e| alpha * acc + e); - let numerator = e - interpolant.eval(subgroup_x); - let denominator = points.iter().map(|&(x, _)| subgroup_x - x).product(); + .fold(F::Extension::ZERO, |acc, &e| alpha * acc + e.into()); + let numerator = e - interpolant.eval(subgroup_x.into()); + let denominator = points + .iter() + .map(|&(x, _)| F::Extension::from_basefield(subgroup_x) - x) + .product(); numerator / denominator } fn fri_verifier_query_round( - interpolant: &PolynomialCoeffs, - points: &[(F, F)], - alpha: F, + interpolant: &PolynomialCoeffs, + points: &[(F::Extension, F::Extension)], + alpha: F::Extension, initial_merkle_roots: &[Hash], proof: &FriProof, challenger: &mut Challenger, n: usize, - betas: &[F], + betas: &[F::Extension], round_proof: &FriQueryRound, config: &FriConfig, -) -> Result<()> { - let mut evaluations: Vec> = Vec::new(); +) -> Result<()> +where + F: Extendable, +{ + let mut evaluations: Vec> = Vec::new(); let x = challenger.get_challenge(); let mut domain_size = n; let mut x_index = x.to_canonical_u64() as usize % n; @@ -212,7 +232,7 @@ fn fri_verifier_query_round( evals.insert(x_index & (arity - 1), e_x); evaluations.push(evals); verify_merkle_proof( - evaluations[i].clone(), + flatten(&evaluations[i]), x_index >> arity_bits, proof.commit_phase_merkle_roots[i], &round_proof.steps[i].merkle_proof, @@ -246,7 +266,7 @@ fn fri_verifier_query_round( // Final check of FRI. After all the reductions, we check that the final polynomial is equal // to the one sent by the prover. ensure!( - proof.final_poly.eval(subgroup_x) == purported_eval, + proof.final_poly.eval(subgroup_x.into()) == purported_eval, "Final polynomial evaluation is invalid." ); diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 0354eda2..98b4a3d2 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget}; @@ -36,12 +37,30 @@ impl Challenger { self.input_buffer.push(element); } + pub fn observe_extension_element(&mut self, element: &F::Extension) + where + F: Extendable, + { + for &e in &element.to_basefield_array() { + self.observe_element(e); + } + } + pub fn observe_elements(&mut self, elements: &[F]) { for &element in elements { self.observe_element(element); } } + pub fn observe_extension_elements(&mut self, elements: &[F::Extension]) + where + F: Extendable, + { + for element in elements { + self.observe_extension_element(element); + } + } + pub fn observe_hash(&mut self, hash: &Hash) { self.observe_elements(&hash.elements) } @@ -76,6 +95,13 @@ impl Challenger { (0..n).map(|_| self.get_challenge()).collect() } + pub fn get_n_extension_challenges(&mut self, n: usize) -> Vec + where + F: Extendable, + { + (0..n).map(|_| self.get_extension_challenge()).collect() + } + pub fn get_hash(&mut self) -> Hash { Hash { elements: [ @@ -87,6 +113,15 @@ impl Challenger { } } + pub fn get_extension_challenge(&mut self) -> F::Extension + where + F: Extendable, + { + let mut arr = [F::ZERO; D]; + arr.copy_from_slice(&self.get_n_challenges(D)); + F::Extension::from_basefield_array(arr) + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs(&mut self) { if self.input_buffer.is_empty() { diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 4775d5ac..7b7df924 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,6 +1,8 @@ use anyhow::Result; use rayon::prelude::*; +use crate::field::extension_field::Extendable; +use crate::field::extension_field::FieldExtension; use crate::field::field::Field; use crate::field::lagrange::interpolant; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; @@ -13,6 +15,7 @@ use crate::timed; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; +pub const EXTENSION_DEGREE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, @@ -76,17 +79,20 @@ impl ListPolynomialCommitment { pub fn open( &self, - points: &[F], + points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>) { + ) -> (OpeningProof, Vec>) + where + F: Extendable, + { assert_eq!(self.rate_bits, config.rate_bits); assert_eq!(config.blinding.len(), 1); assert_eq!(self.blinding, config.blinding[0]); for p in points { assert_ne!( p.exp_usize(self.degree), - F::ONE, + F::Extension::ONE, "Opening point is in the subgroup." ); } @@ -96,15 +102,17 @@ impl ListPolynomialCommitment { .map(|&x| { self.polynomials .iter() - .map(|p| p.eval(x)) + .map(|p| p.to_extension().eval(x)) .collect::>() }) .collect::>(); for evals in &evaluations { - challenger.observe_elements(evals); + for e in evals { + challenger.observe_extension_element(e); + } } - let alpha = challenger.get_challenge(); + let alpha = challenger.get_extension_challenge(); // Scale polynomials by `alpha`. let composition_poly = self @@ -112,7 +120,7 @@ impl ListPolynomialCommitment { .iter() .rev() .fold(PolynomialCoeffs::zero(self.degree), |acc, p| { - &(&acc * alpha) + &p + &(&acc * alpha) + &p.to_extension() }); // Scale evaluations by `alpha`. let composition_evals = evaluations @@ -125,7 +133,7 @@ impl ListPolynomialCommitment { let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(self.rate_bits); let lde_quotient_values = lde_quotient .clone() - .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); + .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR.into()); let fri_proof = fri_proof( &[&self.merkle_tree], @@ -146,10 +154,13 @@ impl ListPolynomialCommitment { pub fn batch_open( commitments: &[&Self], - points: &[F], + points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>>) { + ) -> (OpeningProof, Vec>>) + where + F: Extendable, + { let degree = commitments[0].degree; assert_eq!(config.blinding.len(), commitments.len()); for (i, commitment) in commitments.iter().enumerate() { @@ -166,7 +177,7 @@ impl ListPolynomialCommitment { for p in points { assert_ne!( p.exp_usize(degree), - F::ONE, + F::Extension::ONE, "Opening point is in the subgroup." ); } @@ -176,26 +187,30 @@ impl ListPolynomialCommitment { .map(|&x| { commitments .iter() - .map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::>()) + .map(move |c| { + c.polynomials + .iter() + .map(|p| p.to_extension().eval(x)) + .collect::>() + }) .collect::>() }) .collect::>(); for evals_per_point in &evaluations { for evals in evals_per_point { - challenger.observe_elements(evals); + challenger.observe_extension_elements(evals); } } - let alpha = challenger.get_challenge(); + let alpha = challenger.get_extension_challenge(); // Scale polynomials by `alpha`. let composition_poly = commitments .iter() .flat_map(|c| &c.polynomials) .rev() - .map(|p| p.clone().into()) .fold(PolynomialCoeffs::zero(degree), |acc, p| { - &(&acc * alpha) + &p + &(&acc * alpha) + &p.to_extension() }); // Scale evaluations by `alpha`. let composition_evals = &evaluations @@ -204,16 +219,16 @@ impl ListPolynomialCommitment { v.iter() .flatten() .rev() - .fold(F::ZERO, |acc, &e| acc * alpha + e) + .fold(F::Extension::ZERO, |acc, &e| acc * alpha + e) }) .collect::>(); let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits); - let lde_quotient_values = lde_quotient - .clone() - .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); + let lde_quotient_values = lde_quotient.clone().coset_fft(F::Extension::from_basefield( + F::MULTIPLICATIVE_GROUP_GENERATOR, + )); let fri_proof = fri_proof( &commitments @@ -237,10 +252,13 @@ impl ListPolynomialCommitment { pub fn batch_open_plonk( commitments: &[&Self; 5], - points: &[F], + points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>) { + ) -> (OpeningProof, Vec>) + where + F: Extendable, + { let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config); let opening_sets = evaluations .par_iter_mut() @@ -261,10 +279,13 @@ impl ListPolynomialCommitment { /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. fn compute_quotient( - points: &[F], - evals: &[F], - poly: &PolynomialCoeffs, - ) -> PolynomialCoeffs { + points: &[F::Extension], + evals: &[F::Extension], + poly: &PolynomialCoeffs, + ) -> PolynomialCoeffs + where + F: Extendable, + { let pairs = points .iter() .zip(evals) @@ -274,7 +295,7 @@ impl ListPolynomialCommitment { let interpolant = interpolant(&pairs); let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| { - &acc * &PolynomialCoeffs::new(vec![-x, F::ONE]) + &acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE]) }); let numerator = poly - &interpolant; let (mut quotient, rem) = numerator.div_rem(&denominator); @@ -284,28 +305,28 @@ impl ListPolynomialCommitment { } } -pub struct OpeningProof { +pub struct OpeningProof> { fri_proof: FriProof, // TODO: Get the degree from `CommonCircuitData` instead. quotient_degree: usize, } -impl OpeningProof { +impl> OpeningProof { pub fn verify( &self, - points: &[F], - evaluations: &[Vec>], + points: &[F::Extension], + evaluations: &[Vec>], merkle_roots: &[Hash], challenger: &mut Challenger, fri_config: &FriConfig, ) -> Result<()> { for evals_per_point in evaluations { for evals in evals_per_point { - challenger.observe_elements(evals); + challenger.observe_extension_elements(evals); } } - let alpha = challenger.get_challenge(); + let alpha = challenger.get_extension_challenge(); let scaled_evals = evaluations .par_iter() @@ -313,7 +334,7 @@ impl OpeningProof { v.iter() .flatten() .rev() - .fold(F::ZERO, |acc, &e| acc * alpha + e) + .fold(F::Extension::ZERO, |acc, &e| acc * alpha + e) }) .collect::>(); @@ -343,19 +364,19 @@ mod tests { use super::*; - fn gen_random_test_case( + fn gen_random_test_case>( k: usize, degree_log: usize, num_points: usize, - ) -> (Vec>, Vec) { + ) -> (Vec>, Vec) { let degree = 1 << degree_log; let polys = (0..k) .map(|_| PolynomialCoeffs::new(F::rand_vec(degree))) .collect(); - let mut points = F::rand_vec(num_points); + let mut points = F::Extension::rand_vec(num_points); while points.iter().any(|&x| x.exp_usize(degree).is_one()) { - points = F::rand_vec(num_points); + points = F::Extension::rand_vec(num_points); } (polys, points) diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 630a4816..37143ceb 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,6 +1,7 @@ use std::cmp::max; use std::ops::{Add, Mul, Sub}; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; use crate::util::{log2_ceil, log2_strict}; @@ -167,6 +168,13 @@ impl PolynomialCoeffs { .into(); modified_poly.fft() } + + pub fn to_extension(&self) -> PolynomialCoeffs + where + F: Extendable, + { + PolynomialCoeffs::new(self.coeffs.iter().map(|&c| c.into()).collect()) + } } impl PartialEq for PolynomialCoeffs { diff --git a/src/proof.rs b/src/proof.rs index ff28abc7..ef3b9705 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,6 +1,7 @@ +use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; -use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof}; +use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, EXTENSION_DEGREE}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; @@ -54,7 +55,7 @@ impl HashTarget { } } -pub struct Proof { +pub struct Proof> { /// Merkle root of LDEs of wire values. pub wires_root: Hash, /// Merkle root of LDEs of Z, in the context of Plonk's permutation argument. @@ -63,7 +64,7 @@ pub struct Proof { pub quotient_polys_root: Hash, /// Purported values of each polynomial at each challenge point. - pub openings: Vec>, + pub openings: Vec>, /// A FRI argument for each FRI query. pub opening_proof: OpeningProof, @@ -86,8 +87,8 @@ pub struct ProofTarget { /// Evaluations and Merkle proof produced by the prover in a FRI query step. // TODO: Implement FriQueryStepTarget -pub struct FriQueryStep { - pub evals: Vec, +pub struct FriQueryStep> { + pub evals: Vec, pub merkle_proof: MerkleProof, } @@ -100,18 +101,18 @@ pub struct FriInitialTreeProof { /// Proof for a FRI query round. // TODO: Implement FriQueryRoundTarget -pub struct FriQueryRound { +pub struct FriQueryRound> { pub initial_trees_proof: FriInitialTreeProof, pub steps: Vec>, } -pub struct FriProof { +pub struct FriProof> { /// A Merkle root for each reduced polynomial in the commit phase. pub commit_phase_merkle_roots: Vec>, /// Query rounds proofs pub query_round_proofs: Vec>, /// The final polynomial in coefficient form. - pub final_poly: PolynomialCoeffs, + pub final_poly: PolynomialCoeffs, /// Witness showing that the prover did PoW. pub pow_witness: F, } diff --git a/src/prover.rs b/src/prover.rs index ee4edbb1..b93031ac 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -4,12 +4,13 @@ use log::info; use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; +use crate::field::extension_field::Extendable; use crate::field::fft::ifft; use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; -use crate::polynomial::commitment::ListPolynomialCommitment; +use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; @@ -21,7 +22,7 @@ use crate::witness::PartialWitness; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; -pub(crate) fn prove( +pub(crate) fn prove>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, @@ -109,7 +110,7 @@ pub(crate) fn prove( challenger.observe_hash("ient_polys_commitment.merkle_tree.root); - let zetas = challenger.get_n_challenges(config.num_challenges); + let zetas = challenger.get_n_extension_challenges(config.num_challenges); let (opening_proof, openings) = timed!( ListPolynomialCommitment::batch_open_plonk( From adf5c2d4ecc8e0afa7812120f631ef667d65a892 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 15:44:50 +0200 Subject: [PATCH 2/8] Const generics everywhere --- src/bin/bench_recursion.rs | 4 +-- src/circuit_data.rs | 14 ++++++--- src/fri/mod.rs | 4 +-- src/fri/prover.rs | 22 ++++++--------- src/fri/verifier.rs | 43 +++++++++------------------- src/polynomial/commitment.rs | 55 ++++++++++++++++++------------------ src/proof.rs | 14 ++++----- src/prover.rs | 4 +-- 8 files changed, 72 insertions(+), 88 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index a9b0aa0c..12af72df 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -20,7 +20,7 @@ fn main() { // change this to info or warn later. env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); - bench_prove::(); + bench_prove::(); // bench_field_mul::(); @@ -29,7 +29,7 @@ fn main() { // bench_gmimc::(); } -fn bench_prove>() { +fn bench_prove, const D: usize>() { let gmimc_gate = GMiMCGate::::with_automatic_constants(); let config = CircuitConfig { diff --git a/src/circuit_data.rs b/src/circuit_data.rs index f9bdf420..cae1501f 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -56,8 +56,11 @@ pub struct CircuitData { pub(crate) common: CommonCircuitData, } -impl> CircuitData { - pub fn prove(&self, inputs: PartialWitness) -> Proof { +impl CircuitData { + pub fn prove(&self, inputs: PartialWitness) -> Proof + where + F: Extendable, + { prove(&self.prover_only, &self.common, inputs) } @@ -78,8 +81,11 @@ pub struct ProverCircuitData { pub(crate) common: CommonCircuitData, } -impl> ProverCircuitData { - pub fn prove(&self, inputs: PartialWitness) -> Proof { +impl ProverCircuitData { + pub fn prove(&self, inputs: PartialWitness) -> Proof + where + F: Extendable, + { prove(&self.prover_only, &self.common, inputs) } } diff --git a/src/fri/mod.rs b/src/fri/mod.rs index f00fe8f8..a81fda1e 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -100,9 +100,9 @@ mod tests { PolynomialValues::new(coset_lde.values.into_iter().map(|x| F2::from(x)).collect()); let root = tree.root; let mut challenger = Challenger::new(); - let proof = fri_proof( + let proof = fri_proof::( &[&tree], - &coeffs.to_extension::(), + &coeffs.to_extension::<2>(), &coset_lde, &mut challenger, &config, diff --git a/src/fri/prover.rs b/src/fri/prover.rs index cfbf40e1..21e24296 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -11,7 +11,7 @@ use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, H use crate::util::reverse_index_bits_in_place; /// Builds a FRI proof. -pub fn fri_proof( +pub fn fri_proof, const D: usize>( initial_merkle_trees: &[&MerkleTree], // Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero. lde_polynomial_coeffs: &PolynomialCoeffs, @@ -19,10 +19,7 @@ pub fn fri_proof( lde_polynomial_values: &PolynomialValues, challenger: &mut Challenger, config: &FriConfig, -) -> FriProof -where - F: Extendable, -{ +) -> FriProof { let n = lde_polynomial_values.values.len(); assert_eq!(lde_polynomial_coeffs.coeffs.len(), n); @@ -50,15 +47,12 @@ where } } -fn fri_committed_trees( +fn fri_committed_trees, const D: usize>( polynomial_coeffs: &PolynomialCoeffs, polynomial_values: &PolynomialValues, challenger: &mut Challenger, config: &FriConfig, -) -> (Vec>, PolynomialCoeffs) -where - F: Extendable, -{ +) -> (Vec>, PolynomialCoeffs) { let mut values = polynomial_values.clone(); let mut coeffs = polynomial_coeffs.clone(); @@ -120,25 +114,25 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { .expect("Proof of work failed.") } -fn fri_prover_query_rounds>( +fn fri_prover_query_rounds, const D: usize>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, config: &FriConfig, -) -> Vec> { +) -> Vec> { (0..config.num_query_rounds) .map(|_| fri_prover_query_round(initial_merkle_trees, trees, challenger, n, config)) .collect() } -fn fri_prover_query_round>( +fn fri_prover_query_round, const D: usize>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, config: &FriConfig, -) -> FriQueryRound { +) -> FriQueryRound { let mut query_steps = Vec::new(); let x = challenger.get_challenge(); let mut x_index = x.to_canonical_u64() as usize % n; diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 09819743..ed090ed3 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -13,16 +13,13 @@ use anyhow::{ensure, Result}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. -fn compute_evaluation( +fn compute_evaluation, const D: usize>( x: F, old_x_index: usize, arity_bits: usize, last_evals: &[F::Extension], beta: F::Extension, -) -> F::Extension -where - F: Extendable, -{ +) -> F::Extension { debug_assert_eq!(last_evals.len(), 1 << arity_bits); let g = F::primitive_root_of_unity(arity_bits); @@ -43,14 +40,11 @@ where interpolate(&points, beta, &barycentric_weights) } -fn fri_verify_proof_of_work( - proof: &FriProof, +fn fri_verify_proof_of_work, const D: usize>( + proof: &FriProof, challenger: &mut Challenger, config: &FriConfig, -) -> Result<()> -where - F: Extendable, -{ +) -> Result<()> { let hash = hash_n_to_1( challenger .get_hash() @@ -70,20 +64,17 @@ where Ok(()) } -pub fn verify_fri_proof( +pub fn verify_fri_proof, const D: usize>( purported_degree_log: usize, // Point-evaluation pairs for polynomial commitments. points: &[(F::Extension, F::Extension)], // Scaling factor to combine polynomials. alpha: F::Extension, initial_merkle_roots: &[Hash], - proof: &FriProof, + proof: &FriProof, challenger: &mut Challenger, config: &FriConfig, -) -> Result<()> -where - F: Extendable, -{ +) -> Result<()> { let total_arities = config.reduction_arity_bits.iter().sum::(); ensure!( purported_degree_log @@ -149,17 +140,14 @@ fn fri_verify_initial_proof( Ok(()) } -fn fri_combine_initial( +fn fri_combine_initial, const D: usize>( proof: &FriInitialTreeProof, alpha: F::Extension, interpolant: &PolynomialCoeffs, points: &[(F::Extension, F::Extension)], subgroup_x: F, config: &FriConfig, -) -> F::Extension -where - F: Extendable, -{ +) -> F::Extension { let e = proof .evals_proofs .iter() @@ -175,21 +163,18 @@ where numerator / denominator } -fn fri_verifier_query_round( +fn fri_verifier_query_round, const D: usize>( interpolant: &PolynomialCoeffs, points: &[(F::Extension, F::Extension)], alpha: F::Extension, initial_merkle_roots: &[Hash], - proof: &FriProof, + proof: &FriProof, challenger: &mut Challenger, n: usize, betas: &[F::Extension], - round_proof: &FriQueryRound, + round_proof: &FriQueryRound, config: &FriConfig, -) -> Result<()> -where - F: Extendable, -{ +) -> Result<()> { let mut evaluations: Vec> = Vec::new(); let x = challenger.get_challenge(); let mut domain_size = n; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 7b7df924..8a386d6a 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -15,7 +15,6 @@ use crate::timed; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; -pub const EXTENSION_DEGREE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, @@ -77,14 +76,14 @@ impl ListPolynomialCommitment { &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] } - pub fn open( + pub fn open( &self, points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>) + ) -> (OpeningProof, Vec>) where - F: Extendable, + F: Extendable, { assert_eq!(self.rate_bits, config.rate_bits); assert_eq!(config.blinding.len(), 1); @@ -152,14 +151,14 @@ impl ListPolynomialCommitment { ) } - pub fn batch_open( + pub fn batch_open( commitments: &[&Self], points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>>) + ) -> (OpeningProof, Vec>>) where - F: Extendable, + F: Extendable, { let degree = commitments[0].degree; assert_eq!(config.blinding.len(), commitments.len()); @@ -250,14 +249,14 @@ impl ListPolynomialCommitment { ) } - pub fn batch_open_plonk( + pub fn batch_open_plonk( commitments: &[&Self; 5], points: &[F::Extension], challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>) + ) -> (OpeningProof, Vec>) where - F: Extendable, + F: Extendable, { let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config); let opening_sets = evaluations @@ -278,13 +277,13 @@ impl ListPolynomialCommitment { /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient( + fn compute_quotient( points: &[F::Extension], evals: &[F::Extension], poly: &PolynomialCoeffs, ) -> PolynomialCoeffs where - F: Extendable, + F: Extendable, { let pairs = points .iter() @@ -305,13 +304,13 @@ impl ListPolynomialCommitment { } } -pub struct OpeningProof> { - fri_proof: FriProof, +pub struct OpeningProof, const D: usize> { + fri_proof: FriProof, // TODO: Get the degree from `CommonCircuitData` instead. quotient_degree: usize, } -impl> OpeningProof { +impl, const D: usize> OpeningProof { pub fn verify( &self, points: &[F::Extension], @@ -364,7 +363,7 @@ mod tests { use super::*; - fn gen_random_test_case>( + fn gen_random_test_case, const D: usize>( k: usize, degree_log: usize, num_points: usize, @@ -396,10 +395,10 @@ mod tests { num_query_rounds: 3, blinding: vec![false], }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); + let (polys, points) = gen_random_test_case::(k, degree_log, num_points); let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, false); - let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config); + let (proof, evaluations) = lpc.open::<2>(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -423,10 +422,10 @@ mod tests { num_query_rounds: 3, blinding: vec![true], }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); + let (polys, points) = gen_random_test_case::(k, degree_log, num_points); let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, true); - let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config); + let (proof, evaluations) = lpc.open::<2>(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -452,15 +451,15 @@ mod tests { num_query_rounds: 3, blinding: vec![false, false, false], }; - let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, false); let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, false); - let (proof, evaluations) = ListPolynomialCommitment::batch_open( + let (proof, evaluations) = ListPolynomialCommitment::batch_open::<2>( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), @@ -495,15 +494,15 @@ mod tests { num_query_rounds: 3, blinding: vec![true, false, true], }; - let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, true); let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, true); - let (proof, evaluations) = ListPolynomialCommitment::batch_open( + let (proof, evaluations) = ListPolynomialCommitment::batch_open::<2>( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), diff --git a/src/proof.rs b/src/proof.rs index ef3b9705..03e78f16 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -55,7 +55,7 @@ impl HashTarget { } } -pub struct Proof> { +pub struct Proof, const D: usize> { /// Merkle root of LDEs of wire values. pub wires_root: Hash, /// Merkle root of LDEs of Z, in the context of Plonk's permutation argument. @@ -67,7 +67,7 @@ pub struct Proof> { pub openings: Vec>, /// A FRI argument for each FRI query. - pub opening_proof: OpeningProof, + pub opening_proof: OpeningProof, } pub struct ProofTarget { @@ -87,7 +87,7 @@ pub struct ProofTarget { /// Evaluations and Merkle proof produced by the prover in a FRI query step. // TODO: Implement FriQueryStepTarget -pub struct FriQueryStep> { +pub struct FriQueryStep, const D: usize> { pub evals: Vec, pub merkle_proof: MerkleProof, } @@ -101,16 +101,16 @@ pub struct FriInitialTreeProof { /// Proof for a FRI query round. // TODO: Implement FriQueryRoundTarget -pub struct FriQueryRound> { +pub struct FriQueryRound, const D: usize> { pub initial_trees_proof: FriInitialTreeProof, - pub steps: Vec>, + pub steps: Vec>, } -pub struct FriProof> { +pub struct FriProof, const D: usize> { /// A Merkle root for each reduced polynomial in the commit phase. pub commit_phase_merkle_roots: Vec>, /// Query rounds proofs - pub query_round_proofs: Vec>, + pub query_round_proofs: Vec>, /// The final polynomial in coefficient form. pub final_poly: PolynomialCoeffs, /// Witness showing that the prover did PoW. diff --git a/src/prover.rs b/src/prover.rs index b93031ac..f9847af5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -22,11 +22,11 @@ use crate::witness::PartialWitness; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; -pub(crate) fn prove>( +pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, -) -> Proof { +) -> Proof { let fri_config = &common_data.config.fri_config; let start_proof_gen = Instant::now(); From 9cd00532ceb7e92a800be39bf1370702c6e1c33d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 16:06:47 +0200 Subject: [PATCH 3/8] Generic tests --- src/bin/bench_recursion.rs | 1 - src/circuit_data.rs | 2 +- src/fri/mod.rs | 61 +++++++++++++---- src/fri/prover.rs | 2 - src/fri/verifier.rs | 3 +- src/polynomial/commitment.rs | 124 +++++++++++++++++++++++++++-------- src/proof.rs | 2 +- src/prover.rs | 2 +- 8 files changed, 147 insertions(+), 50 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 12af72df..efab1dd8 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -9,7 +9,6 @@ use plonky2::fri::FriConfig; use plonky2::gates::constant::ConstantGate; use plonky2::gates::gmimc::GMiMCGate; use plonky2::hash::GMIMC_ROUNDS; -use plonky2::polynomial::commitment::EXTENSION_DEGREE; use plonky2::prover::PLONK_BLINDING; use plonky2::witness::PartialWitness; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index cae1501f..d45192cb 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -4,7 +4,7 @@ use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; use crate::merkle_tree::MerkleTree; -use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE}; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; use crate::verifier::verify; diff --git a/src/fri/mod.rs b/src/fri/mod.rs index a81fda1e..480980ca 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -54,29 +54,25 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; - use crate::field::extension_field::{flatten, FieldExtension}; + use crate::field::extension_field::{flatten, Extendable, FieldExtension}; use crate::field::fft::ifft; use crate::field::field::Field; use crate::fri::prover::fri_proof; use crate::fri::verifier::verify_fri_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; - use crate::polynomial::commitment::EXTENSION_DEGREE; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::reverse_index_bits_in_place; use anyhow::Result; use rand::rngs::ThreadRng; use rand::Rng; - fn test_fri( + fn check_fri, const D: usize>( degree_log: usize, rate_bits: usize, reduction_arity_bits: Vec, num_query_rounds: usize, ) -> Result<()> { - type F = CrandallField; - type F2 = QuadraticCrandallField; - let n = 1 << degree_log; let coeffs = PolynomialCoeffs::new(F::rand_vec(n)).lde(rate_bits); let coset_lde = coeffs.clone().coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); @@ -96,13 +92,18 @@ mod tests { reverse_index_bits_in_place(&mut leaves); MerkleTree::new(leaves, false) }; - let coset_lde = - PolynomialValues::new(coset_lde.values.into_iter().map(|x| F2::from(x)).collect()); + let coset_lde = PolynomialValues::new( + coset_lde + .values + .into_iter() + .map(|x| F::Extension::from(x)) + .collect(), + ); let root = tree.root; let mut challenger = Challenger::new(); - let proof = fri_proof::( + let proof = fri_proof::( &[&tree], - &coeffs.to_extension::<2>(), + &coeffs.to_extension::(), &coset_lde, &mut challenger, &config, @@ -112,7 +113,7 @@ mod tests { verify_fri_proof( degree_log, &[], - F2::ONE, + F::Extension::ONE, &[root], &proof, &mut challenger, @@ -133,14 +134,13 @@ mod tests { arities } - #[test] - fn test_fri_multi_params() -> Result<()> { + fn check_fri_multi_params, const D: usize>() -> Result<()> { let mut rng = rand::thread_rng(); for degree_log in 1..6 { for rate_bits in 0..3 { for num_query_round in 0..4 { for _ in 0..3 { - test_fri( + check_fri::( degree_log, rate_bits, gen_arities(degree_log, &mut rng), @@ -152,4 +152,37 @@ mod tests { } Ok(()) } + + mod base { + use super::*; + type F = CrandallField; + const D: usize = 1; + + #[test] + fn test_fri_multi_params() -> Result<()> { + check_fri_multi_params::() + } + } + + mod quadratic { + use super::*; + type F = CrandallField; + const D: usize = 2; + + #[test] + fn test_fri_multi_params() -> Result<()> { + check_fri_multi_params::() + } + } + + mod quartic { + use super::*; + type F = CrandallField; + const D: usize = 4; + + #[test] + fn test_fri_multi_params() -> Result<()> { + check_fri_multi_params::() + } + } } diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 21e24296..cd95c4a6 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -5,7 +5,6 @@ use crate::hash::hash_n_to_1; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; -use crate::polynomial::commitment::EXTENSION_DEGREE; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, Hash}; use crate::util::reverse_index_bits_in_place; @@ -136,7 +135,6 @@ fn fri_prover_query_round, const D: usize>( let mut query_steps = Vec::new(); let x = challenger.get_challenge(); let mut x_index = x.to_canonical_u64() as usize % n; - dbg!(x_index, n); let initial_proof = initial_merkle_trees .iter() .map(|t| (t.get(x_index).to_vec(), t.prove(x_index))) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index ed090ed3..351f119c 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -5,7 +5,7 @@ use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; -use crate::polynomial::commitment::{EXTENSION_DEGREE, SALT_SIZE}; +use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash}; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -35,7 +35,6 @@ fn compute_evaluation, const D: usize>( .zip(evals) .map(|(y, e)| ((x * y).into(), e)) .collect::>(); - dbg!(&points); let barycentric_weights = barycentric_weights(&points); interpolate(&points, beta, &barycentric_weights) } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 8a386d6a..48b63eff 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -381,10 +381,7 @@ mod tests { (polys, points) } - #[test] - fn test_polynomial_commitment() -> Result<()> { - type F = CrandallField; - + fn check_polynomial_commitment, const D: usize>() -> Result<()> { let k = 10; let degree_log = 11; let num_points = 3; @@ -395,10 +392,10 @@ mod tests { num_query_rounds: 3, blinding: vec![false], }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); + let (polys, points) = gen_random_test_case::(k, degree_log, num_points); let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, false); - let (proof, evaluations) = lpc.open::<2>(&points, &mut Challenger::new(), &fri_config); + let (proof, evaluations) = lpc.open::(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -408,10 +405,8 @@ mod tests { ) } - #[test] - fn test_polynomial_commitment_blinding() -> Result<()> { - type F = CrandallField; - + fn check_polynomial_commitment_blinding, const D: usize>() -> Result<()> + { let k = 10; let degree_log = 11; let num_points = 3; @@ -422,10 +417,10 @@ mod tests { num_query_rounds: 3, blinding: vec![true], }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); + let (polys, points) = gen_random_test_case::(k, degree_log, num_points); let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, true); - let (proof, evaluations) = lpc.open::<2>(&points, &mut Challenger::new(), &fri_config); + let (proof, evaluations) = lpc.open::(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -435,10 +430,7 @@ mod tests { ) } - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - type F = CrandallField; - + fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { let k0 = 10; let k1 = 3; let k2 = 7; @@ -451,15 +443,15 @@ mod tests { num_query_rounds: 3, blinding: vec![false, false, false], }; - let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k1, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k2, degree_log, num_points); let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, false); let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, false); - let (proof, evaluations) = ListPolynomialCommitment::batch_open::<2>( + let (proof, evaluations) = ListPolynomialCommitment::batch_open::( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), @@ -478,10 +470,8 @@ mod tests { ) } - #[test] - fn test_batch_polynomial_commitment_blinding() -> Result<()> { - type F = CrandallField; - + fn check_batch_polynomial_commitment_blinding, const D: usize>( + ) -> Result<()> { let k0 = 10; let k1 = 3; let k2 = 7; @@ -494,15 +484,15 @@ mod tests { num_query_rounds: 3, blinding: vec![true, false, true], }; - let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k1, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k2, degree_log, num_points); let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, true); let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, true); - let (proof, evaluations) = ListPolynomialCommitment::batch_open::<2>( + let (proof, evaluations) = ListPolynomialCommitment::batch_open::( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), @@ -520,4 +510,82 @@ mod tests { &fri_config, ) } + + mod base { + use super::*; + type F = CrandallField; + const D: usize = 1; + + #[test] + fn test_polynomial_commitment() -> Result<()> { + check_polynomial_commitment::() + } + + #[test] + fn test_polynomial_commitment_blinding() -> Result<()> { + check_polynomial_commitment_blinding::() + } + + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::() + } + + #[test] + fn test_batch_polynomial_commitment_blinding() -> Result<()> { + check_batch_polynomial_commitment_blinding::() + } + } + + mod quadratic { + use super::*; + type F = CrandallField; + const D: usize = 2; + + #[test] + fn test_polynomial_commitment() -> Result<()> { + check_polynomial_commitment::() + } + + #[test] + fn test_polynomial_commitment_blinding() -> Result<()> { + check_polynomial_commitment_blinding::() + } + + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::() + } + + #[test] + fn test_batch_polynomial_commitment_blinding() -> Result<()> { + check_batch_polynomial_commitment_blinding::() + } + } + + mod quartic { + use super::*; + type F = CrandallField; + const D: usize = 4; + + #[test] + fn test_polynomial_commitment() -> Result<()> { + check_polynomial_commitment::() + } + + #[test] + fn test_polynomial_commitment_blinding() -> Result<()> { + check_polynomial_commitment_blinding::() + } + + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::() + } + + #[test] + fn test_batch_polynomial_commitment_blinding() -> Result<()> { + check_batch_polynomial_commitment_blinding::() + } + } } diff --git a/src/proof.rs b/src/proof.rs index 03e78f16..b12c24ec 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,7 +1,7 @@ use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; -use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, EXTENSION_DEGREE}; +use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; diff --git a/src/prover.rs b/src/prover.rs index f9847af5..4a570598 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -10,7 +10,7 @@ use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; -use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE}; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; From 96a880193c534f07811e5099e7b300041a54db0a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 16:09:22 +0200 Subject: [PATCH 4/8] Clippy --- src/field/crandall_field.rs | 2 +- src/field/field.rs | 1 - src/fri/prover.rs | 2 +- src/polynomial/polynomial.rs | 5 +++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 261ec0f1..46dcb172 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -6,7 +6,7 @@ use num::Integer; use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; -use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::extension_field::Extendable; use crate::field::field::Field; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; diff --git a/src/field/field.rs b/src/field/field.rs index 4025c968..3521edf8 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -5,7 +5,6 @@ use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num::Integer; -use rand::rngs::OsRng; use rand::Rng; use crate::util::bits_u64; diff --git a/src/fri/prover.rs b/src/fri/prover.rs index cd95c4a6..d1eeadcf 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -1,4 +1,4 @@ -use crate::field::extension_field::{flatten, unflatten, Extendable, FieldExtension}; +use crate::field::extension_field::{flatten, unflatten, Extendable}; use crate::field::field::Field; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 37143ceb..f0faaf11 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,10 +1,10 @@ use std::cmp::max; use std::ops::{Add, Mul, Sub}; -use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; -use crate::util::{log2_ceil, log2_strict}; +use crate::util::log2_strict; /// A polynomial in point-value form. /// @@ -237,6 +237,7 @@ impl Mul for &PolynomialCoeffs { impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; + #[allow(clippy::suspicious_arithmetic_impl)] fn mul(self, rhs: Self) -> Self::Output { let new_len = (self.len() + rhs.len()).next_power_of_two(); let a = self.padded(new_len); From 4f6f2192ab67d9770894517b2e00d891afb64fb9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 16:23:44 +0200 Subject: [PATCH 5/8] Minor fixes --- src/field/extension_field/quadratic.rs | 5 +- src/field/extension_field/quartic.rs | 7 +- src/fri/mod.rs | 14 ++-- src/plonk_challenger.rs | 14 ++-- src/polynomial/commitment.rs | 97 ++++++++------------------ 5 files changed, 42 insertions(+), 95 deletions(-) diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index a9a9e5b1..acc66def 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -111,10 +111,7 @@ impl Field for QuadraticCrandallField { // otherwise the FFT doesn't commute with field inclusion. fn primitive_root_of_unity(n_log: usize) -> Self { if n_log <= CrandallField::TWO_ADICITY { - Self([ - CrandallField::primitive_root_of_unity(n_log), - CrandallField::ZERO, - ]) + CrandallField::primitive_root_of_unity(n_log).into() } else { // The root of unity isn't in the base field so we need to compute it manually. assert!(n_log <= Self::TWO_ADICITY); diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index d72fbeb8..49235a06 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -149,12 +149,7 @@ impl Field for QuarticCrandallField { // otherwise the FFT doesn't commute with field inclusion. fn primitive_root_of_unity(n_log: usize) -> Self { if n_log <= CrandallField::TWO_ADICITY { - Self([ - CrandallField::primitive_root_of_unity(n_log), - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - ]) + CrandallField::primitive_root_of_unity(n_log).into() } else { // The root of unity isn't in the base field so we need to compute it manually. assert!(n_log <= Self::TWO_ADICITY); diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 480980ca..d701c421 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -96,7 +96,7 @@ mod tests { coset_lde .values .into_iter() - .map(|x| F::Extension::from(x)) + .map(F::Extension::from) .collect(), ); let root = tree.root; @@ -155,34 +155,28 @@ mod tests { mod base { use super::*; - type F = CrandallField; - const D: usize = 1; #[test] fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() + check_fri_multi_params::() } } mod quadratic { use super::*; - type F = CrandallField; - const D: usize = 2; #[test] fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() + check_fri_multi_params::() } } mod quartic { use super::*; - type F = CrandallField; - const D: usize = 4; #[test] fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() + check_fri_multi_params::() } } } diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 98b4a3d2..67957e13 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -95,13 +95,6 @@ impl Challenger { (0..n).map(|_| self.get_challenge()).collect() } - pub fn get_n_extension_challenges(&mut self, n: usize) -> Vec - where - F: Extendable, - { - (0..n).map(|_| self.get_extension_challenge()).collect() - } - pub fn get_hash(&mut self) -> Hash { Hash { elements: [ @@ -122,6 +115,13 @@ impl Challenger { F::Extension::from_basefield_array(arr) } + pub fn get_n_extension_challenges(&mut self, n: usize) -> Vec + where + F: Extendable, + { + (0..n).map(|_| self.get_extension_challenge()).collect() + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs(&mut self) { if self.input_buffer.is_empty() { diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 48b63eff..7fc5b96c 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -511,81 +511,42 @@ mod tests { ) } + macro_rules! tests_commitments { + ($F:ty, $D:expr) => { + use super::*; + + #[test] + fn test_polynomial_commitment() -> Result<()> { + check_polynomial_commitment::<$F, $D>() + } + + #[test] + fn test_polynomial_commitment_blinding() -> Result<()> { + check_polynomial_commitment_blinding::<$F, $D>() + } + + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::<$F, $D>() + } + + #[test] + fn test_batch_polynomial_commitment_blinding() -> Result<()> { + check_batch_polynomial_commitment_blinding::<$F, $D>() + } + }; + } + mod base { - use super::*; - type F = CrandallField; - const D: usize = 1; - - #[test] - fn test_polynomial_commitment() -> Result<()> { - check_polynomial_commitment::() - } - - #[test] - fn test_polynomial_commitment_blinding() -> Result<()> { - check_polynomial_commitment_blinding::() - } - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::() - } - - #[test] - fn test_batch_polynomial_commitment_blinding() -> Result<()> { - check_batch_polynomial_commitment_blinding::() - } + tests_commitments!(crate::field::crandall_field::CrandallField, 1); } mod quadratic { - use super::*; - type F = CrandallField; - const D: usize = 2; - - #[test] - fn test_polynomial_commitment() -> Result<()> { - check_polynomial_commitment::() - } - - #[test] - fn test_polynomial_commitment_blinding() -> Result<()> { - check_polynomial_commitment_blinding::() - } - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::() - } - - #[test] - fn test_batch_polynomial_commitment_blinding() -> Result<()> { - check_batch_polynomial_commitment_blinding::() - } + tests_commitments!(crate::field::crandall_field::CrandallField, 2); } mod quartic { use super::*; - type F = CrandallField; - const D: usize = 4; - - #[test] - fn test_polynomial_commitment() -> Result<()> { - check_polynomial_commitment::() - } - - #[test] - fn test_polynomial_commitment_blinding() -> Result<()> { - check_polynomial_commitment_blinding::() - } - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::() - } - - #[test] - fn test_batch_polynomial_commitment_blinding() -> Result<()> { - check_batch_polynomial_commitment_blinding::() - } + tests_commitments!(crate::field::crandall_field::CrandallField, 4); } } From 8737c8d5b9621108eed85642bd515d02af5de919 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 16:36:14 +0200 Subject: [PATCH 6/8] revert extension field order --- src/field/extension_field/quadratic.rs | 2 +- src/field/extension_field/quartic.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index acc66def..0b6f26f0 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -88,7 +88,7 @@ impl Field for QuadraticCrandallField { const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]); // Does not fit in 64-bits. - const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading. + const ORDER: u64 = 0; const TWO_ADICITY: usize = 29; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([CrandallField(3), CrandallField::ONE]); const POWER_OF_TWO_GENERATOR: Self = diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 49235a06..ecf9bfd2 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -114,7 +114,7 @@ impl Field for QuarticCrandallField { ]); // Does not fit in 64-bits. - const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading. + const ORDER: u64 = 0; const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ CrandallField(3), From 1cbd12edbd549f88a39ecf23f6a6216322199aac Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 18 May 2021 22:22:15 +0200 Subject: [PATCH 7/8] Fixes based on PR comments --- src/field/extension_field/mod.rs | 1 + src/field/extension_field/quadratic.rs | 11 ++--------- src/field/extension_field/quartic.rs | 11 ++--------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 7d1f1adf..67329c3c 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -86,6 +86,7 @@ pub fn unflatten(l: &[F]) -> Vec where F: Extendable, { + debug_assert_eq!(l.len() % D, 0); l.chunks_exact(D) .map(|c| { let mut arr = [F::ZERO; D]; diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 0b6f26f0..90c41f9d 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -32,8 +32,6 @@ pub trait QuadraticFieldExtension: Self::from_canonical_representation([a0, a1 * z]) } - - fn scalar_mul(&self, c: Self::BaseField) -> Self; } #[derive(Copy, Clone)] @@ -52,11 +50,6 @@ impl QuadraticFieldExtension for QuadraticCrandallField { fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self { Self(v) } - - fn scalar_mul(&self, c: Self::BaseField) -> Self { - let [a0, a1] = self.to_canonical_representation(); - Self([a0 * c, a1 * c]) - } } impl From<::BaseField> for QuadraticCrandallField { @@ -104,7 +97,7 @@ impl Field for QuadraticCrandallField { let a_pow_r = a_pow_r_minus_1 * *self; debug_assert!(a_pow_r.is_in_basefield()); - Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) + Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } // It's important that the primitive roots of unity are the same as the ones in the base field, @@ -273,7 +266,7 @@ mod tests { assert_eq!(-x, F::ZERO - x); assert_eq!( x + x, - x.scalar_mul(::BaseField::TWO) + x * ::BaseField::TWO.into() ); assert_eq!(x * (-x), -x.square()); assert_eq!(x + y, y + x); diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index ecf9bfd2..c317db32 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -38,8 +38,6 @@ pub trait QuarticFieldExtension: Field + From<::B Self::from_canonical_representation([b0, b1, b2, b3]) } - - fn scalar_mul(&self, c: Self::BaseField) -> Self; } #[derive(Copy, Clone)] @@ -58,11 +56,6 @@ impl QuarticFieldExtension for QuarticCrandallField { fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self { Self(v) } - - fn scalar_mul(&self, c: Self::BaseField) -> Self { - let [a0, a1, a2, a3] = self.to_canonical_representation(); - Self([a0 * c, a1 * c, a2 * c, a3 * c]) - } } impl From<::BaseField> for QuarticCrandallField { @@ -142,7 +135,7 @@ impl Field for QuarticCrandallField { let a_pow_r = a_pow_r_minus_1 * *self; debug_assert!(a_pow_r.is_in_basefield()); - Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) + Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } // It's important that the primitive roots of unity are the same as the ones in the base field, @@ -329,7 +322,7 @@ mod tests { assert_eq!(-x, F::ZERO - x); assert_eq!( x + x, - x.scalar_mul(::BaseField::TWO) + x * ::BaseField::TWO.into() ); assert_eq!(x * (-x), -x.square()); assert_eq!(x + y, y + x); From e806d86f867eb397f23750005b1c74601cdde935 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 19 May 2021 09:35:39 +0200 Subject: [PATCH 8/8] Remove custom `primitive_root_of_unity` in extension fields by modifying the generators. --- src/field/extension_field/quadratic.rs | 44 +++++++++++++--------- src/field/extension_field/quartic.rs | 51 +++++++++++++++----------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 90c41f9d..3382a487 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -83,9 +83,15 @@ impl Field for QuadraticCrandallField { // Does not fit in 64-bits. const ORDER: u64 = 0; const TWO_ADICITY: usize = 29; - const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([CrandallField(3), CrandallField::ONE]); + const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ + CrandallField(6483724566312148654), + CrandallField(12194665049945415126), + ]); + // Chosen so that when raised to the power `1<<(Self::TWO_ADICITY-Self::BaseField::TWO_ADICITY)`, + // we get `Self::BaseField::POWER_OF_TWO_GENERATOR`. This makes `primitive_root_of_unity` coherent + // with the base field which implies that the FFT commutes with field inclusion. const POWER_OF_TWO_GENERATOR: Self = - Self([CrandallField::ZERO, CrandallField(7889429148549342301)]); + Self([CrandallField::ZERO, CrandallField(14420468973723774561)]); // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { @@ -100,22 +106,6 @@ impl Field for QuadraticCrandallField { Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } - // It's important that the primitive roots of unity are the same as the ones in the base field, - // otherwise the FFT doesn't commute with field inclusion. - fn primitive_root_of_unity(n_log: usize) -> Self { - if n_log <= CrandallField::TWO_ADICITY { - CrandallField::primitive_root_of_unity(n_log).into() - } else { - // The root of unity isn't in the base field so we need to compute it manually. - assert!(n_log <= Self::TWO_ADICITY); - let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base - } - } - fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } @@ -311,4 +301,22 @@ mod tests { F::ONE ); } + + #[test] + fn test_power_of_two_gen() { + type F = QuadraticCrandallField; + // F::ORDER = 2^29 * 2762315674048163 * 229454332791453 + 1 + assert_eq!( + F::MULTIPLICATIVE_GROUP_GENERATOR + .exp_usize(2762315674048163) + .exp_usize(229454332791453), + F::POWER_OF_TWO_GENERATOR + ); + assert_eq!( + F::POWER_OF_TWO_GENERATOR.exp_usize( + 1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY) + ), + ::BaseField::POWER_OF_TWO_GENERATOR.into() + ); + } } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index c317db32..41a49a26 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -110,16 +110,19 @@ impl Field for QuarticCrandallField { const ORDER: u64 = 0; const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ - CrandallField(3), - CrandallField::ONE, - CrandallField::ZERO, - CrandallField::ZERO, + CrandallField(12476589904174392631), + CrandallField(896937834427772243), + CrandallField(7795248119019507390), + CrandallField(9005769437373554825), ]); + // Chosen so that when raised to the power `1<<(Self::TWO_ADICITY-Self::BaseField::TWO_ADICITY)`, + // we get `Self::BaseField::POWER_OF_TWO_GENERATOR`. This makes `primitive_root_of_unity` coherent + // with the base field which implies that the FFT commutes with field inclusion. const POWER_OF_TWO_GENERATOR: Self = Self([ CrandallField::ZERO, CrandallField::ZERO, CrandallField::ZERO, - CrandallField(14096607364803438105), + CrandallField(15170983443234254033), ]); // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. @@ -138,22 +141,6 @@ impl Field for QuarticCrandallField { Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } - // It's important that the primitive roots of unity are the same as the ones in the base field, - // otherwise the FFT doesn't commute with field inclusion. - fn primitive_root_of_unity(n_log: usize) -> Self { - if n_log <= CrandallField::TWO_ADICITY { - CrandallField::primitive_root_of_unity(n_log).into() - } else { - // The root of unity isn't in the base field so we need to compute it manually. - assert!(n_log <= Self::TWO_ADICITY); - let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base - } - } - fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } @@ -370,4 +357,26 @@ mod tests { F::ONE ); } + + #[test] + fn test_power_of_two_gen() { + type F = QuarticCrandallField; + // F::ORDER = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1 + assert_eq!( + exp_naive( + exp_naive( + F::MULTIPLICATIVE_GROUP_GENERATOR, + 1090552343587053358839971118999869 + ), + 98885475095492590491252558464653635 + ), + F::POWER_OF_TWO_GENERATOR + ); + assert_eq!( + F::POWER_OF_TWO_GENERATOR.exp_usize( + 1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY) + ), + ::BaseField::POWER_OF_TWO_GENERATOR.into() + ); + } }