diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index b8a7c20f..3ed199dd 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -118,14 +118,16 @@ impl, const D: usize> CircuitBuilder { "Number of reductions should be non-zero." ); + let precomputed_reduced_evals = + PrecomputedReducedEvalsTarget::from_os_and_alpha(os, alpha, self); for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { context!( self, &format!("verify {}'th FRI query", i), self.fri_verifier_query_round( - os, zeta, alpha, + precomputed_reduced_evals, initial_merkle_roots, proof, challenger, @@ -162,9 +164,9 @@ impl, const D: usize> CircuitBuilder { &mut self, proof: &FriInitialTreeProofTarget, alpha: ExtensionTarget, - os: &OpeningSetTarget, zeta: ExtensionTarget, subgroup_x: Target, + precomputed_reduced_evals: PrecomputedReducedEvalsTarget, common_data: &CommonCircuitData, ) -> ExtensionTarget { assert!(D > 1, "Not implemented for D=1."); @@ -192,19 +194,9 @@ impl, const D: usize> CircuitBuilder { ) .map(|&e| self.convert_to_ext(e)) .collect::>(); - let single_openings = os - .constants - .iter() - .chain(&os.plonk_sigmas) - .chain(&os.wires) - .chain(&os.quotient_polys) - .chain(&os.partial_products) - .copied() - .collect::>(); - let mut single_numerator = alpha.reduce(&single_evals, self); - // TODO: Precompute the rhs as it is the same in all FRI rounds. - let rhs = alpha.reduce(&single_openings, self); - single_numerator = self.sub_extension(single_numerator, rhs); + let single_composition_eval = alpha.reduce(&single_evals, self); + let single_numerator = + self.sub_extension(single_composition_eval, precomputed_reduced_evals.single); let single_denominator = self.sub_extension(subgroup_x, zeta); let quotient = self.div_unsafe_extension(single_numerator, single_denominator); sum = self.add_extension(sum, quotient); @@ -217,14 +209,15 @@ impl, const D: usize> CircuitBuilder { .take(common_data.zs_range().end) .map(|&e| self.convert_to_ext(e)) .collect::>(); - let zs_composition_eval = alpha.clone().reduce(&zs_evals, self); + let zs_composition_eval = alpha.reduce(&zs_evals, self); let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); let zeta_right = self.mul_extension(g, zeta); - let zs_ev_zeta = alpha.clone().reduce(&os.plonk_zs, self); - let zs_ev_zeta_right = alpha.reduce(&os.plonk_zs_right, self); let interpol_val = self.interpolate2( - [(zeta, zs_ev_zeta), (zeta_right, zs_ev_zeta_right)], + [ + (zeta, precomputed_reduced_evals.zs), + (zeta_right, precomputed_reduced_evals.zs_right), + ], subgroup_x, ); let zs_numerator = self.sub_extension(zs_composition_eval, interpol_val); @@ -240,9 +233,9 @@ impl, const D: usize> CircuitBuilder { fn fri_verifier_query_round( &mut self, - os: &OpeningSetTarget, zeta: ExtensionTarget, alpha: ExtensionTarget, + precomputed_reduced_evals: PrecomputedReducedEvalsTarget, initial_merkle_roots: &[HashTarget], proof: &FriProofTarget, challenger: &mut RecursiveChallenger, @@ -289,9 +282,9 @@ impl, const D: usize> CircuitBuilder { self.fri_combine_initial( &round_proof.initial_trees_proof, alpha, - os, zeta, subgroup_x, + precomputed_reduced_evals, common_data, ) ) @@ -366,3 +359,39 @@ impl, const D: usize> CircuitBuilder { self.assert_equal_extension(eval, purported_eval); } } + +#[derive(Copy, Clone)] +struct PrecomputedReducedEvalsTarget { + pub single: ExtensionTarget, + pub zs: ExtensionTarget, + pub zs_right: ExtensionTarget, +} + +impl PrecomputedReducedEvalsTarget { + fn from_os_and_alpha>( + os: &OpeningSetTarget, + alpha: ExtensionTarget, + builder: &mut CircuitBuilder, + ) -> Self { + let mut alpha = ReducingFactorTarget::new(alpha); + let single = alpha.reduce( + &os.constants + .iter() + .chain(&os.plonk_sigmas) + .chain(&os.wires) + .chain(&os.quotient_polys) + .chain(&os.partial_products) + .copied() + .collect::>(), + builder, + ); + let zs = alpha.reduce(&os.plonk_zs, builder); + let zs_right = alpha.reduce(&os.plonk_zs_right, builder); + + Self { + single, + zs, + zs_right, + } + } +} diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index df0c33fc..22e0f24c 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -112,11 +112,12 @@ pub fn verify_fri_proof, const D: usize>( "Number of reductions should be non-zero." ); + let precomputed_reduced_evals = PrecomputedReducedEvals::from_os_and_alpha(os, alpha); for round_proof in &proof.query_round_proofs { fri_verifier_query_round( - os, zeta, alpha, + precomputed_reduced_evals, initial_merkle_roots, &proof, challenger, @@ -142,12 +143,43 @@ fn fri_verify_initial_proof( Ok(()) } +/// Holds the reduced (by `alpha`) evaluations at `zeta` for the polynomial opened just at +/// zeta, for `Z` at zeta and for `Z` at `g*zeta`. +#[derive(Copy, Clone)] +struct PrecomputedReducedEvals, const D: usize> { + pub single: F::Extension, + pub zs: F::Extension, + pub zs_right: F::Extension, +} + +impl, const D: usize> PrecomputedReducedEvals { + fn from_os_and_alpha(os: &OpeningSet, alpha: F::Extension) -> Self { + let mut alpha = ReducingFactor::new(alpha); + let single = alpha.reduce( + os.constants + .iter() + .chain(&os.plonk_sigmas) + .chain(&os.wires) + .chain(&os.quotient_polys) + .chain(&os.partial_products), + ); + let zs = alpha.reduce(os.plonk_zs.iter()); + let zs_right = alpha.reduce(os.plonk_zs_right.iter()); + + Self { + single, + zs, + zs_right, + } + } +} + fn fri_combine_initial, const D: usize>( proof: &FriInitialTreeProof, alpha: F::Extension, - os: &OpeningSet, zeta: F::Extension, subgroup_x: F, + precomputed_reduced_evals: PrecomputedReducedEvals, common_data: &CommonCircuitData, ) -> F::Extension { let config = &common_data.config; @@ -174,19 +206,8 @@ fn fri_combine_initial, const D: usize>( [common_data.partial_products_range()], ) .map(|&e| F::Extension::from_basefield(e)); - let single_openings = os - .constants - .iter() - .chain(&os.plonk_sigmas) - .chain(&os.wires) - .chain(&os.quotient_polys) - .chain(&os.partial_products); - let single_diffs = single_evals - .into_iter() - .zip(single_openings) - .map(|(e, &o)| e - o) - .collect::>(); - let single_numerator = alpha.reduce(single_diffs.iter()); + let single_composition_eval = alpha.reduce(single_evals); + let single_numerator = single_composition_eval - precomputed_reduced_evals.single; let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; alpha.reset(); @@ -197,12 +218,12 @@ fn fri_combine_initial, const D: usize>( .iter() .map(|&e| F::Extension::from_basefield(e)) .take(common_data.zs_range().end); - let zs_composition_eval = alpha.clone().reduce(zs_evals); + let zs_composition_eval = alpha.reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolate2( [ - (zeta, alpha.clone().reduce(os.plonk_zs.iter())), - (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), + (zeta, precomputed_reduced_evals.zs), + (zeta_right, precomputed_reduced_evals.zs_right), ], subgroup_x, ); @@ -215,9 +236,9 @@ fn fri_combine_initial, const D: usize>( } fn fri_verifier_query_round, const D: usize>( - os: &OpeningSet, zeta: F::Extension, alpha: F::Extension, + precomputed_reduced_evals: PrecomputedReducedEvals, initial_merkle_roots: &[Hash], proof: &FriProof, challenger: &mut Challenger, @@ -248,9 +269,9 @@ fn fri_verifier_query_round, const D: usize>( fri_combine_initial( &round_proof.initial_trees_proof, alpha, - os, zeta, subgroup_x, + precomputed_reduced_evals, common_data, ) } else {