diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 7198ebf6..20cd3dcb 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -206,4 +206,11 @@ impl, const D: usize> CircuitBuilder { } b } + + pub fn convert_to_ext(&mut self, t: Target) -> ExtensionTarget { + let zero = self.zero(); + let mut arr = [zero; D]; + arr[0] = t; + ExtensionTarget(arr) + } } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 81cc59ab..f0a2338e 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -2,6 +2,7 @@ use anyhow::{ensure, Result}; use itertools::izip; use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; @@ -11,8 +12,10 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::plonk_common::reduce_with_iter; use crate::proof::{ - FriInitialTreeProof, FriProof, FriProofTarget, FriQueryRound, Hash, OpeningSet, + FriInitialTreeProof, FriInitialTreeProofTarget, FriProof, FriProofTarget, FriQueryRound, Hash, + HashTarget, OpeningSet, OpeningSetTarget, }; +use crate::target::Target; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { @@ -119,88 +122,89 @@ impl, const D: usize> CircuitBuilder { // Ok(()) // } // - // fn fri_verify_initial_proof( - // x_index: usize, - // proof: &FriInitialTreeProof, - // initial_merkle_roots: &[Hash], - // ) -> Result<()> { - // for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { - // verify_merkle_proof(evals.clone(), x_index, root, merkle_proof, false)?; - // } - // - // Ok(()) - // } - // - // fn fri_combine_initial, const D: usize>( - // proof: &FriInitialTreeProof, - // alpha: F::Extension, - // os: &OpeningSet, - // zeta: F::Extension, - // subgroup_x: F, - // config: &FriConfig, - // ) -> F::Extension { - // assert!(D > 1, "Not implemented for D=1."); - // let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; - // let subgroup_x = F::Extension::from_basefield(subgroup_x); - // let mut alpha_powers = alpha.powers(); - // let mut sum = F::Extension::ZERO; - // - // // We will add three terms to `sum`: - // // - one for polynomials opened at `x` only - // // - one for polynomials opened at `x` and `g x` - // // - one for polynomials opened at `x` and its conjugate - // - // let evals = [0, 1, 4] - // .iter() - // .flat_map(|&i| proof.unsalted_evals(i, config)) - // .map(|&e| F::Extension::from_basefield(e)); - // let openings = os - // .constants - // .iter() - // .chain(&os.plonk_sigmas) - // .chain(&os.quotient_polys); - // let numerator = izip!(evals, openings, &mut alpha_powers) - // .map(|(e, &o, a)| a * (e - o)) - // .sum::(); - // let denominator = subgroup_x - zeta; - // sum += numerator / denominator; - // - // let ev: F::Extension = proof - // .unsalted_evals(3, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - // let zs_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - // ( - // zeta_right, - // reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - // ), - // ]); - // let numerator = ev - zs_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); - // sum += numerator / denominator; - // - // let ev: F::Extension = proof - // .unsalted_evals(2, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_frob = zeta.frobenius(); - // let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - // let wires_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), - // (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), - // ]); - // let numerator = ev - wires_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - // sum += numerator / denominator; - // - // sum - // } + + fn fri_verify_initial_proof( + &mut self, + x_index: Target, + proof: &FriInitialTreeProofTarget, + initial_merkle_roots: &[HashTarget], + ) { + for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { + self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof); + } + } + + fn fri_combine_initial( + &mut self, + proof: &FriInitialTreeProofTarget, + alpha: ExtensionTarget, + os: &OpeningSetTarget, + zeta: ExtensionTarget, + subgroup_x: Target, + ) -> ExtensionTarget { + assert!(D > 1, "Not implemented for D=1."); + let config = &self.config.fri_config; + let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; + let subgroup_x = self.convert_to_ext(subgroup_x); + let mut alpha_powers = self.powers(alpha); + let mut sum = self.zero_extension(); + + // We will add three terms to `sum`: + // - one for polynomials opened at `x` only + // - one for polynomials opened at `x` and `g x` + // - one for polynomials opened at `x` and its conjugate + + let evals = [0, 1, 4] + .iter() + .flat_map(|&i| proof.unsalted_evals(i, config)) + .map(|&e| F::Extension::from_basefield(e)); + let openings = os + .constants + .iter() + .chain(&os.plonk_sigmas) + .chain(&os.quotient_polys); + let numerator = izip!(evals, openings, &mut alpha_powers) + .map(|(e, &o, a)| a * (e - o)) + .sum::(); + let denominator = subgroup_x - zeta; + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(3, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; + let zs_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ]); + let numerator = ev - zs_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(2, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_frob = zeta.frobenius(); + let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + let wires_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), + (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), + ]); + let numerator = ev - wires_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum += numerator / denominator; + + sum + } // // fn fri_verifier_query_round, const D: usize>( // os: &OpeningSet, diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index a214df3f..f152f77d 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::gates::arithmetic::ArithmeticGate; @@ -226,6 +227,30 @@ impl, const D: usize> CircuitBuilder { } } +/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. +#[derive(Clone)] +pub struct PowersTarget { + base: ExtensionTarget, + current: ExtensionTarget, +} + +impl, const D: usize> PowersTarget { + fn next(&mut self, builder: &mut CircuitBuilder) -> Option> { + let result = self.current; + self.current = builder.mul_extension(self.base, self.current); + Some(result) + } +} + +impl, const D: usize> CircuitBuilder { + pub fn powers(&mut self, base: ExtensionTarget) -> PowersTarget { + PowersTarget { + base, + current: self.one_extension(), + } + } +} + struct QuotientGenerator { numerator: Target, denominator: Target, diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index d5ab8a78..7d50f466 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -62,7 +62,7 @@ impl, const D: usize> CircuitBuilder { leaf_data: Vec, leaf_index: Target, merkle_root: HashTarget, - proof: MerkleProofTarget, + proof: &MerkleProofTarget, ) { let zero = self.zero(); let height = proof.siblings.len(); @@ -71,7 +71,7 @@ impl, const D: usize> CircuitBuilder { let mut state: HashTarget = self.hash_or_noop(leaf_data); let mut acc_leaf_index = zero; - for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { + for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) { let gate = self .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); diff --git a/src/proof.rs b/src/proof.rs index 332eb941..65634f4c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -35,6 +35,7 @@ impl Hash { } /// Represents a ~256 bit hash output. +#[derive(Copy, Clone, Debug)] pub struct HashTarget { pub(crate) elements: [Target; 4], } @@ -107,6 +108,13 @@ pub struct FriInitialTreeProofTarget { pub evals_proofs: Vec<(Vec, MerkleProofTarget)>, } +impl FriInitialTreeProofTarget { + pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[Target] { + let evals = &self.evals_proofs[i].0; + &evals[..evals.len() - config.salt_size(i)] + } +} + /// Proof for a FRI query round. pub struct FriQueryRound, const D: usize> { pub initial_trees_proof: FriInitialTreeProof,