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(