From 2b8432b11dcb411ad5dea1cfcbf6e4177a43980f Mon Sep 17 00:00:00 2001 From: Sai <135601871+sai-deng@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:25:42 +0800 Subject: [PATCH] Sync upstream (#109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Co-authored-by: Hamy Ratoanina Co-authored-by: Daniel-Aaron-Bloom <76709210+Daniel-Aaron-Bloom@users.noreply.github.com> Co-authored-by: nuno <41233686+qope@users.noreply.github.com> Co-authored-by: Matthias Görgens Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> Co-authored-by: Gio <102917377+gio256@users.noreply.github.com> Co-authored-by: Robin Salen --- plonky2/Cargo.toml | 2 +- ...ld_merkle_tree.rs => batch_merkle_tree.rs} | 14 +- plonky2/src/fri/batch_oracle.rs | 610 ------------------ plonky2/src/fri/batch_prover.rs | 463 ------------- plonky2/src/fri/batch_recursive_verifier.rs | 332 ---------- plonky2/src/fri/batch_verifier.rs | 251 ------- plonky2/src/fri/mod.rs | 4 - plonky2/src/hash/field_merkle_tree.rs | 337 ---------- 8 files changed, 8 insertions(+), 2005 deletions(-) rename plonky2/benches/{field_merkle_tree.rs => batch_merkle_tree.rs} (73%) delete mode 100644 plonky2/src/fri/batch_oracle.rs delete mode 100644 plonky2/src/fri/batch_prover.rs delete mode 100644 plonky2/src/fri/batch_recursive_verifier.rs delete mode 100644 plonky2/src/fri/batch_verifier.rs delete mode 100644 plonky2/src/hash/field_merkle_tree.rs diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 5470949953..b58ee4caa4 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -65,7 +65,7 @@ name = "field_arithmetic" harness = false [[bench]] -name = "field_merkle_tree" +name = "batch_merkle_tree" harness = false [[bench]] diff --git a/plonky2/benches/field_merkle_tree.rs b/plonky2/benches/batch_merkle_tree.rs similarity index 73% rename from plonky2/benches/field_merkle_tree.rs rename to plonky2/benches/batch_merkle_tree.rs index dfaa301321..87c0ac940e 100644 --- a/plonky2/benches/field_merkle_tree.rs +++ b/plonky2/benches/batch_merkle_tree.rs @@ -2,7 +2,7 @@ mod allocator; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::hash::field_merkle_tree::FieldMerkleTree; +use plonky2::hash::batch_merkle_tree::BatchMerkleTree; use plonky2::hash::hash_types::RichField; use plonky2::hash::keccak::KeccakHash; use plonky2::hash::poseidon::PoseidonHash; @@ -13,9 +13,9 @@ const ELEMS_PER_LEAF_1: usize = 70; const ELEMS_PER_LEAF_2: usize = 5; const ELEMS_PER_LEAF_3: usize = 100; -pub(crate) fn bench_field_merkle_tree>(c: &mut Criterion) { - let mut group = c.benchmark_group(&format!( - "field-merkle-tree<{}, {}>", +pub(crate) fn bench_batch_merkle_tree>(c: &mut Criterion) { + let mut group = c.benchmark_group(format!( + "batch-merkle-tree<{}, {}>", type_name::(), type_name::() )); @@ -29,14 +29,14 @@ pub(crate) fn bench_field_merkle_tree>(c: &mut Criter vec![F::rand_vec(ELEMS_PER_LEAF_2); size >> 1], vec![F::rand_vec(ELEMS_PER_LEAF_3); size >> 2], ]; - b.iter(|| FieldMerkleTree::::new(black_box(leaves.clone()), black_box(5))); + b.iter(|| BatchMerkleTree::::new(black_box(leaves.clone()), black_box(5))); }); } } fn criterion_benchmark(c: &mut Criterion) { - bench_field_merkle_tree::(c); - bench_field_merkle_tree::>(c); + bench_batch_merkle_tree::(c); + bench_batch_merkle_tree::>(c); } criterion_group!(benches, criterion_benchmark); diff --git a/plonky2/src/fri/batch_oracle.rs b/plonky2/src/fri/batch_oracle.rs deleted file mode 100644 index 1be347a653..0000000000 --- a/plonky2/src/fri/batch_oracle.rs +++ /dev/null @@ -1,610 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::{format, vec::Vec}; - -use itertools::Itertools; -use plonky2_field::extension::Extendable; -use plonky2_field::fft::FftRootTable; -use plonky2_field::packed::PackedField; -use plonky2_field::polynomial::{PolynomialCoeffs, PolynomialValues}; -use plonky2_field::types::Field; -use plonky2_maybe_rayon::*; -use plonky2_util::{log2_strict, reverse_index_bits_in_place}; - -use crate::fri::batch_prover::batch_fri_proof; -use crate::fri::oracle::PolynomialBatch; -use crate::fri::proof::FriProof; -use crate::fri::structure::{FriBatchInfo, FriInstanceInfo}; -use crate::fri::FriParams; -use crate::hash::field_merkle_tree::FieldMerkleTree; -use crate::hash::hash_types::RichField; -use crate::iop::challenger::Challenger; -use crate::plonk::config::GenericConfig; -use crate::timed; -use crate::util::reducing::ReducingFactor; -use crate::util::timing::TimingTree; -use crate::util::{reverse_bits, transpose}; - -/// Represents a batch FRI oracle, i.e. a batch of polynomials with different degrees which have -/// been Merkle-ized in a Field Merkle Tree. -#[derive(Eq, PartialEq, Debug)] -pub struct BatchFriOracle, C: GenericConfig, const D: usize> -{ - pub polynomials: Vec>, - pub field_merkle_tree: FieldMerkleTree, - // The degree bits of each polynomial group. - pub degree_bits: Vec, - pub rate_bits: usize, - pub blinding: bool, -} - -impl, C: GenericConfig, const D: usize> - BatchFriOracle -{ - /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. - pub fn from_values( - values: Vec>, - rate_bits: usize, - blinding: bool, - cap_height: usize, - timing: &mut TimingTree, - fft_root_table: &[Option<&FftRootTable>], - ) -> Self { - let coeffs = timed!( - timing, - "IFFT", - values.into_par_iter().map(|v| v.ifft()).collect::>() - ); - - Self::from_coeffs( - coeffs, - rate_bits, - blinding, - cap_height, - timing, - fft_root_table, - ) - } - - /// Creates a list polynomial commitment for the polynomials `polynomials`. - pub fn from_coeffs( - polynomials: Vec>, - rate_bits: usize, - blinding: bool, - cap_height: usize, - timing: &mut TimingTree, - fft_root_table: &[Option<&FftRootTable>], - ) -> Self { - let mut degree_bits = polynomials - .iter() - .map(|p| log2_strict(p.len())) - .collect_vec(); - assert!(degree_bits.windows(2).all(|pair| { pair[0] >= pair[1] })); - - let num_polynomials = polynomials.len(); - let mut group_start = 0; - let mut leaves = Vec::new(); - - for (i, d) in degree_bits.iter().enumerate() { - if i == num_polynomials - 1 || *d > degree_bits[i + 1] { - let lde_values = timed!( - timing, - "FFT + blinding", - PolynomialBatch::::lde_values( - &polynomials[group_start..i + 1], - rate_bits, - blinding, - fft_root_table[i] - ) - ); - - let mut leaf_group = timed!(timing, "transpose LDEs", transpose(&lde_values)); - reverse_index_bits_in_place(&mut leaf_group); - leaves.push(leaf_group); - - group_start = i + 1; - } - } - - let field_merkle_tree = timed!( - timing, - "build Field Merkle tree", - FieldMerkleTree::new(leaves, cap_height) - ); - - degree_bits.sort_unstable(); - degree_bits.dedup(); - degree_bits.reverse(); - assert_eq!(field_merkle_tree.leaves.len(), degree_bits.len()); - Self { - polynomials, - field_merkle_tree, - degree_bits, - rate_bits, - blinding, - } - } - - /// Produces a batch opening proof. - pub fn prove_openings( - degree_bits: &[usize], - instances: &[FriInstanceInfo], - oracles: &[&Self], - challenger: &mut Challenger, - fri_params: &FriParams, - timing: &mut TimingTree, - ) -> FriProof { - assert_eq!(degree_bits.len(), instances.len()); - assert!(D > 1, "Not implemented for D=1."); - let alpha = challenger.get_extension_challenge::(); - let mut alpha = ReducingFactor::new(alpha); - - let mut final_lde_polynomial_coeff = Vec::with_capacity(instances.len()); - let mut final_lde_polynomial_values = Vec::with_capacity(instances.len()); - for (i, instance) in instances.iter().enumerate() { - // Final low-degree polynomial that goes into FRI. - let mut final_poly = PolynomialCoeffs::empty(); - - // Each batch `i` consists of an opening point `z_i` and polynomials `{f_ij}_j` to be opened at that point. - // For each batch, we compute the composition polynomial `F_i = sum alpha^j f_ij`, - // where `alpha` is a random challenge in the extension field. - // The final polynomial is then computed as `final_poly = sum_i alpha^(k_i) (F_i(X) - F_i(z_i))/(X-z_i)` - // where the `k_i`s are chosen such that each power of `alpha` appears only once in the final sum. - // There are usually two batches for the openings at `zeta` and `g * zeta`. - // The oracles used in Plonky2 are given in `FRI_ORACLES` in `plonky2/src/plonk/plonk_common.rs`. - for FriBatchInfo { point, polynomials } in &instance.batches { - // Collect the coefficients of all the polynomials in `polynomials`. - let polys_coeff = polynomials.iter().map(|fri_poly| { - &oracles[fri_poly.oracle_index].polynomials[fri_poly.polynomial_index] - }); - let composition_poly = timed!( - timing, - &format!("reduce batch of {} polynomials", polynomials.len()), - alpha.reduce_polys_base(polys_coeff) - ); - let mut quotient = composition_poly.divide_by_linear(*point); - quotient.coeffs.push(F::Extension::ZERO); // pad back to power of two - alpha.shift_poly(&mut final_poly); - final_poly += quotient; - } - - assert_eq!(final_poly.len(), 1 << degree_bits[i]); - let lde_final_poly = final_poly.lde(fri_params.config.rate_bits); - let lde_final_values = timed!( - timing, - &format!("perform final FFT {}", lde_final_poly.len()), - lde_final_poly.coset_fft(F::coset_shift().into()) - ); - final_lde_polynomial_coeff.push(lde_final_poly); - final_lde_polynomial_values.push(lde_final_values); - } - - batch_fri_proof::( - &oracles - .iter() - .map(|o| &o.field_merkle_tree) - .collect::>(), - final_lde_polynomial_coeff[0].clone(), - &final_lde_polynomial_values, - challenger, - fri_params, - timing, - ) - } - - /// Fetches LDE values at the `index * step`th point. - pub fn get_lde_values( - &self, - degree_bits_index: usize, - index: usize, - step: usize, - slice_start: usize, - slice_len: usize, - ) -> &[F] { - let index = index * step; - let index = reverse_bits(index, self.degree_bits[degree_bits_index] + self.rate_bits); - let slice = &self.field_merkle_tree.leaves[degree_bits_index][index]; - &slice[slice_start..slice_start + slice_len] - } - - /// Like `get_lde_values`, but fetches LDE values from a batch of `P::WIDTH` points, and returns - /// packed values. - pub fn get_lde_values_packed

( - &self, - degree_bits_index: usize, - index_start: usize, - step: usize, - slice_start: usize, - slice_len: usize, - ) -> Vec

- where - P: PackedField, - { - let row_wise = (0..P::WIDTH) - .map(|i| { - self.get_lde_values( - degree_bits_index, - index_start + i, - step, - slice_start, - slice_len, - ) - }) - .collect_vec(); - - // This is essentially a transpose, but we will not use the generic transpose method as we - // want inner lists to be of type P, not Vecs which would involve allocation. - let leaf_size = row_wise[0].len(); - (0..leaf_size) - .map(|j| { - let mut packed = P::ZEROS; - packed - .as_slice_mut() - .iter_mut() - .zip(&row_wise) - .for_each(|(packed_i, row_i)| *packed_i = row_i[j]); - packed - }) - .collect_vec() - } -} - -#[cfg(test)] -mod test { - #[cfg(not(feature = "std"))] - use alloc::vec; - - use plonky2_field::goldilocks_field::GoldilocksField; - use plonky2_field::types::Sample; - - use super::*; - use crate::fri::batch_oracle::BatchFriOracle; - use crate::fri::batch_verifier::verify_batch_fri_proof; - use crate::fri::reduction_strategies::FriReductionStrategy; - use crate::fri::structure::{ - FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOpeningBatch, - FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget, FriOracleInfo, FriPolynomialInfo, - }; - use crate::fri::witness_util::set_fri_proof_target; - use crate::fri::FriConfig; - use crate::iop::challenger::RecursiveChallenger; - use crate::iop::witness::PartialWitness; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::PoseidonGoldilocksConfig; - use crate::plonk::prover::prove; - - const D: usize = 2; - - type C = PoseidonGoldilocksConfig; - type F = >::F; - type H = >::Hasher; - - #[test] - fn batch_prove_openings() -> anyhow::Result<()> { - let mut timing = TimingTree::default(); - - let k0 = 9; - let k1 = 8; - let k2 = 6; - let reduction_arity_bits = vec![1, 2, 1]; - let fri_params = FriParams { - config: FriConfig { - rate_bits: 1, - cap_height: 0, - proof_of_work_bits: 0, - reduction_strategy: FriReductionStrategy::Fixed(reduction_arity_bits.clone()), - num_query_rounds: 10, - }, - hiding: false, - degree_bits: k0, - reduction_arity_bits, - }; - - let n0 = 1 << k0; - let n1 = 1 << k1; - let n2 = 1 << k2; - let trace0 = PolynomialValues::new(F::rand_vec(n0)); - let trace1_0 = PolynomialValues::new(F::rand_vec(n1)); - let trace1_1 = PolynomialValues::new(F::rand_vec(n1)); - let trace2 = PolynomialValues::new(F::rand_vec(n2)); - - let trace_oracle: BatchFriOracle = BatchFriOracle::from_values( - vec![ - trace0.clone(), - trace1_0.clone(), - trace1_1.clone(), - trace2.clone(), - ], - fri_params.config.rate_bits, - fri_params.hiding, - fri_params.config.cap_height, - &mut timing, - &[None; 4], - ); - - let mut challenger = Challenger::::new(); - challenger.observe_cap(&trace_oracle.field_merkle_tree.cap); - let zeta = challenger.get_extension_challenge::(); - let eta = challenger.get_extension_challenge::(); - let poly0 = &trace_oracle.polynomials[0]; - let poly1_0 = &trace_oracle.polynomials[1]; - let poly1_1 = &trace_oracle.polynomials[2]; - let poly2 = &trace_oracle.polynomials[3]; - - let mut challenger = Challenger::::new(); - let mut verifier_challenger = challenger.clone(); - - let fri_instance_0 = FriInstanceInfo { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![ - FriBatchInfo { - point: zeta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 0, - }], - }, - FriBatchInfo { - point: eta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 0, - }], - }, - ], - }; - let fri_instance_1 = FriInstanceInfo { - oracles: vec![FriOracleInfo { - num_polys: 2, - blinding: false, - }], - batches: vec![ - FriBatchInfo { - point: zeta, - polynomials: vec![ - FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 1, - }, - FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 2, - }, - ], - }, - FriBatchInfo { - point: eta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 2, - }], - }, - ], - }; - let fri_instance_2 = FriInstanceInfo { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![FriBatchInfo { - point: zeta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 3, - }], - }], - }; - let fri_instances = vec![fri_instance_0, fri_instance_1, fri_instance_2]; - let poly0_zeta = poly0.to_extension::().eval(zeta); - let poly0_eta = poly0.to_extension::().eval(eta); - let fri_opening_batch_0 = FriOpenings { - batches: vec![ - FriOpeningBatch { - values: vec![poly0_zeta], - }, - FriOpeningBatch { - values: vec![poly0_eta], - }, - ], - }; - let poly10_zeta = poly1_0.to_extension::().eval(zeta); - let poly11_zeta = poly1_1.to_extension::().eval(zeta); - let poly11_eta = poly1_1.to_extension::().eval(eta); - let fri_opening_batch_1 = FriOpenings { - batches: vec![ - FriOpeningBatch { - values: vec![poly10_zeta, poly11_zeta], - }, - FriOpeningBatch { - values: vec![poly11_eta], - }, - ], - }; - let poly2_zeta = poly2.to_extension::().eval(zeta); - let fri_opening_batch_2 = FriOpenings { - batches: vec![FriOpeningBatch { - values: vec![poly2_zeta], - }], - }; - let fri_openings = vec![ - fri_opening_batch_0, - fri_opening_batch_1, - fri_opening_batch_2, - ]; - - let proof = BatchFriOracle::prove_openings( - &[k0, k1, k2], - &fri_instances, - &[&trace_oracle], - &mut challenger, - &fri_params, - &mut timing, - ); - - let fri_challenges = verifier_challenger.fri_challenges::( - &proof.commit_phase_merkle_caps, - &proof.final_poly, - proof.pow_witness, - k0, - &fri_params.config, - ); - let degree_bits = [k0, k1, k2]; - let merkle_cap = trace_oracle.field_merkle_tree.cap; - verify_batch_fri_proof::( - °ree_bits, - &fri_instances, - &fri_openings, - &fri_challenges, - &[merkle_cap.clone()], - &proof, - &fri_params, - )?; - - // Test recursive verifier - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config.clone()); - let num_leaves_per_oracle = vec![4]; - let fri_proof_target = builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params); - let zeta_target = builder.constant_extension(zeta); - let eta_target = builder.constant_extension(eta); - let fri_instance_info_target_0 = FriInstanceInfoTarget { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![ - FriBatchInfoTarget { - point: zeta_target, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 0, - }], - }, - FriBatchInfoTarget { - point: eta_target, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 0, - }], - }, - ], - }; - let fri_instance_info_target_1 = FriInstanceInfoTarget { - oracles: vec![FriOracleInfo { - num_polys: 2, - blinding: false, - }], - batches: vec![ - FriBatchInfoTarget { - point: zeta_target, - polynomials: vec![ - FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 1, - }, - FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 2, - }, - ], - }, - FriBatchInfoTarget { - point: eta_target, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 2, - }], - }, - ], - }; - let fri_instance_info_target_2 = FriInstanceInfoTarget { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![FriBatchInfoTarget { - point: zeta_target, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 3, - }], - }], - }; - - let poly0_zeta_target = builder.constant_extension(poly0_zeta); - let poly0_eta_target = builder.constant_extension(poly0_eta); - let fri_opening_batch_0 = FriOpeningsTarget { - batches: vec![ - FriOpeningBatchTarget { - values: vec![poly0_zeta_target], - }, - FriOpeningBatchTarget { - values: vec![poly0_eta_target], - }, - ], - }; - let poly10_zeta_target = builder.constant_extension(poly10_zeta); - let poly11_zeta_target = builder.constant_extension(poly11_zeta); - let poly11_eta_target = builder.constant_extension(poly11_eta); - let fri_opening_batch_1 = FriOpeningsTarget { - batches: vec![ - FriOpeningBatchTarget { - values: vec![poly10_zeta_target, poly11_zeta_target], - }, - FriOpeningBatchTarget { - values: vec![poly11_eta_target], - }, - ], - }; - let poly2_zeta_target = builder.constant_extension(poly2_zeta); - let fri_opening_batch_2 = FriOpeningsTarget { - batches: vec![FriOpeningBatchTarget { - values: vec![poly2_zeta_target], - }], - }; - let fri_openings_target = [ - fri_opening_batch_0, - fri_opening_batch_1, - fri_opening_batch_2, - ]; - - let mut challenger = RecursiveChallenger::::new(&mut builder); - let fri_challenges_target = challenger.fri_challenges( - &mut builder, - &fri_proof_target.commit_phase_merkle_caps, - &fri_proof_target.final_poly, - fri_proof_target.pow_witness, - &fri_params.config, - ); - - let merkle_cap_target = builder.constant_merkle_cap(&merkle_cap); - - let fri_instance_info_target = vec![ - fri_instance_info_target_0, - fri_instance_info_target_1, - fri_instance_info_target_2, - ]; - - builder.verify_batch_fri_proof::( - °ree_bits, - &fri_instance_info_target, - &fri_openings_target, - &fri_challenges_target, - &[merkle_cap_target], - &fri_proof_target, - &fri_params, - ); - - let mut pw = PartialWitness::new(); - set_fri_proof_target(&mut pw, &fri_proof_target, &proof); - - let data = builder.build::(); - let proof = prove::(&data.prover_only, &data.common, pw, &mut timing)?; - data.verify(proof.clone())?; - - Ok(()) - } -} diff --git a/plonky2/src/fri/batch_prover.rs b/plonky2/src/fri/batch_prover.rs deleted file mode 100644 index bf41dff03b..0000000000 --- a/plonky2/src/fri/batch_prover.rs +++ /dev/null @@ -1,463 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -use plonky2_field::extension::flatten; -#[allow(unused_imports)] -use plonky2_field::types::Field; -use plonky2_maybe_rayon::*; -use plonky2_util::reverse_index_bits_in_place; - -use crate::field::extension::{unflatten, Extendable}; -use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; -use crate::fri::prover::{fri_proof_of_work, FriCommitedTrees}; -use crate::fri::FriParams; -use crate::hash::field_merkle_tree::FieldMerkleTree; -use crate::hash::hash_types::RichField; -use crate::hash::merkle_tree::MerkleTree; -use crate::iop::challenger::Challenger; -use crate::plonk::config::GenericConfig; -use crate::plonk::plonk_common::reduce_with_powers; -use crate::timed; -use crate::util::timing::TimingTree; - -/// Builds a batch FRI proof. -pub fn batch_fri_proof, C: GenericConfig, const D: usize>( - initial_merkle_trees: &[&FieldMerkleTree], - lde_polynomial_coeffs: PolynomialCoeffs, - lde_polynomial_values: &[PolynomialValues], - challenger: &mut Challenger, - fri_params: &FriParams, - timing: &mut TimingTree, -) -> FriProof { - let n = lde_polynomial_coeffs.len(); - assert_eq!(lde_polynomial_values[0].len(), n); - // The polynomial vectors should be sorted by degree, from largest to smallest, with no duplicate degrees. - assert!(lde_polynomial_values - .windows(2) - .all(|pair| { pair[0].len() > pair[1].len() })); - - // Commit phase - let (trees, final_coeffs) = timed!( - timing, - "fold codewords in the commitment phase", - batch_fri_committed_trees::( - lde_polynomial_coeffs, - lde_polynomial_values, - challenger, - fri_params, - ) - ); - - // PoW phase - let pow_witness = timed!( - timing, - "find proof-of-work witness", - fri_proof_of_work::(challenger, &fri_params.config) - ); - - // Query phase - let query_round_proofs = batch_fri_prover_query_rounds::( - initial_merkle_trees, - &trees, - challenger, - n, - fri_params, - ); - - FriProof { - commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(), - query_round_proofs, - final_poly: final_coeffs, - pow_witness, - } -} - -pub(crate) fn batch_fri_committed_trees< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - mut final_coeffs: PolynomialCoeffs, - values: &[PolynomialValues], - challenger: &mut Challenger, - fri_params: &FriParams, -) -> FriCommitedTrees { - let mut trees = Vec::with_capacity(fri_params.reduction_arity_bits.len()); - let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR; - let mut polynomial_index = 1; - let mut final_values = values[0].clone(); - for arity_bits in &fri_params.reduction_arity_bits { - let arity = 1 << arity_bits; - - reverse_index_bits_in_place(&mut final_values.values); - let chunked_values = final_values.values.par_chunks(arity).map(flatten).collect(); - let tree = MerkleTree::::new(chunked_values, fri_params.config.cap_height); - - challenger.observe_cap(&tree.cap); - trees.push(tree); - - let beta = challenger.get_extension_challenge::(); - // P(x) = sum_{i>(), - ); - shift = shift.exp_u64(arity as u64); - final_values = final_coeffs.coset_fft(shift.into()); - if polynomial_index != values.len() && final_values.len() == values[polynomial_index].len() - { - final_values = PolynomialValues::new( - final_values - .values - .iter() - .zip(&values[polynomial_index].values) - .map(|(&f, &v)| f * beta + v) - .collect::>(), - ); - polynomial_index += 1; - } - final_coeffs = final_values.clone().coset_ifft(shift.into()); - } - assert_eq!(polynomial_index, values.len()); - - // The coefficients being removed here should always be zero. - final_coeffs - .coeffs - .truncate(final_coeffs.len() >> fri_params.config.rate_bits); - - challenger.observe_extension_elements(&final_coeffs.coeffs); - (trees, final_coeffs) -} - -fn batch_fri_prover_query_rounds< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - initial_merkle_trees: &[&FieldMerkleTree], - trees: &[MerkleTree], - challenger: &mut Challenger, - n: usize, - fri_params: &FriParams, -) -> Vec> { - challenger - .get_n_challenges(fri_params.config.num_query_rounds) - .into_par_iter() - .map(|rand| { - let x_index = rand.to_canonical_u64() as usize % n; - batch_fri_prover_query_round::( - initial_merkle_trees, - trees, - x_index, - fri_params, - ) - }) - .collect() -} - -fn batch_fri_prover_query_round< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - initial_merkle_trees: &[&FieldMerkleTree], - trees: &[MerkleTree], - mut x_index: usize, - fri_params: &FriParams, -) -> FriQueryRound { - let mut query_steps = Vec::with_capacity(trees.len()); - let initial_proof = initial_merkle_trees - .iter() - .map(|t| { - ( - t.values(x_index) - .iter() - .flatten() - .cloned() - .collect::>(), - t.open_batch(x_index), - ) - }) - .collect::>(); - for (i, tree) in trees.iter().enumerate() { - let arity_bits = fri_params.reduction_arity_bits[i]; - let evals = unflatten(tree.get(x_index >> arity_bits)); - let merkle_proof = tree.prove(x_index >> arity_bits); - - query_steps.push(FriQueryStep { - evals, - merkle_proof, - }); - - x_index >>= arity_bits; - } - FriQueryRound { - initial_trees_proof: FriInitialTreeProof { - evals_proofs: initial_proof, - }, - steps: query_steps, - } -} - -#[cfg(test)] -mod tests { - #[cfg(not(feature = "std"))] - use alloc::vec; - - use anyhow::Result; - use itertools::Itertools; - use plonky2_field::goldilocks_field::GoldilocksField; - use plonky2_field::types::{Field64, Sample}; - - use super::*; - use crate::fri::batch_oracle::BatchFriOracle; - use crate::fri::batch_verifier::verify_batch_fri_proof; - use crate::fri::reduction_strategies::FriReductionStrategy; - use crate::fri::structure::{ - FriBatchInfo, FriInstanceInfo, FriOpeningBatch, FriOpenings, FriOracleInfo, - FriPolynomialInfo, - }; - use crate::fri::FriConfig; - use crate::plonk::config::PoseidonGoldilocksConfig; - - const D: usize = 2; - - type C = PoseidonGoldilocksConfig; - type F = >::F; - type H = >::Hasher; - - #[test] - fn single_polynomial() -> Result<()> { - let mut timing = TimingTree::default(); - - let k = 9; - let reduction_arity_bits = vec![1, 2, 1]; - let fri_params = FriParams { - config: FriConfig { - rate_bits: 1, - cap_height: 5, - proof_of_work_bits: 0, - reduction_strategy: FriReductionStrategy::Fixed(reduction_arity_bits.clone()), - num_query_rounds: 10, - }, - hiding: false, - degree_bits: k, - reduction_arity_bits, - }; - - let n = 1 << k; - let trace = PolynomialValues::new((1..n + 1).map(F::from_canonical_i64).collect_vec()); - - let polynomial_batch: BatchFriOracle = BatchFriOracle::from_values( - vec![trace.clone()], - fri_params.config.rate_bits, - fri_params.hiding, - fri_params.config.cap_height, - &mut timing, - &[None], - ); - let poly = &polynomial_batch.polynomials[0]; - let mut challenger = Challenger::::new(); - challenger.observe_cap(&polynomial_batch.field_merkle_tree.cap); - let _alphas = challenger.get_n_challenges(2); - let zeta = challenger.get_extension_challenge::(); - challenger.observe_extension_element::(&poly.to_extension::().eval(zeta)); - let mut verifier_challenger = challenger.clone(); - - let fri_instance: FriInstanceInfo = FriInstanceInfo { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![FriBatchInfo { - point: zeta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index: 0, - }], - }], - }; - let _alpha = challenger.get_extension_challenge::(); - - let composition_poly = poly.mul_extension::(>::Extension::ONE); - let mut quotient = composition_poly.divide_by_linear(zeta); - quotient.coeffs.push(>::Extension::ZERO); - - let lde_final_poly = quotient.lde(fri_params.config.rate_bits); - let lde_final_values = lde_final_poly.coset_fft(F::coset_shift().into()); - - let proof = batch_fri_proof::( - &[&polynomial_batch.field_merkle_tree], - lde_final_poly, - &[lde_final_values], - &mut challenger, - &fri_params, - &mut timing, - ); - - let fri_challenges = verifier_challenger.fri_challenges::( - &proof.commit_phase_merkle_caps, - &proof.final_poly, - proof.pow_witness, - k, - &fri_params.config, - ); - - let fri_opening_batch = FriOpeningBatch { - values: vec![poly.to_extension::().eval(zeta)], - }; - verify_batch_fri_proof::( - &[k], - &[fri_instance], - &[FriOpenings { - batches: vec![fri_opening_batch], - }], - &fri_challenges, - &[polynomial_batch.field_merkle_tree.cap], - &proof, - &fri_params, - ) - } - - #[test] - fn multiple_polynomials() -> Result<()> { - let mut timing = TimingTree::default(); - - let k0 = 9; - let k1 = 8; - let k2 = 6; - let reduction_arity_bits = vec![1, 2, 1]; - let fri_params = FriParams { - config: FriConfig { - rate_bits: 1, - cap_height: 5, - proof_of_work_bits: 0, - reduction_strategy: FriReductionStrategy::Fixed(reduction_arity_bits.clone()), - num_query_rounds: 10, - }, - hiding: false, - degree_bits: k0, - reduction_arity_bits, - }; - - let n0 = 1 << k0; - let n1 = 1 << k1; - let n2 = 1 << k2; - let trace0 = PolynomialValues::new(F::rand_vec(n0)); - let trace1 = PolynomialValues::new(F::rand_vec(n1)); - let trace2 = PolynomialValues::new(F::rand_vec(n2)); - - let trace_oracle: BatchFriOracle = BatchFriOracle::from_values( - vec![trace0.clone(), trace1.clone(), trace2.clone()], - fri_params.config.rate_bits, - fri_params.hiding, - fri_params.config.cap_height, - &mut timing, - &[None; 3], - ); - - let mut challenger = Challenger::::new(); - challenger.observe_cap(&trace_oracle.field_merkle_tree.cap); - let _alphas = challenger.get_n_challenges(2); - let zeta = challenger.get_extension_challenge::(); - let poly0 = &trace_oracle.polynomials[0]; - let poly1 = &trace_oracle.polynomials[1]; - let poly2 = &trace_oracle.polynomials[2]; - challenger.observe_extension_element::(&poly0.to_extension::().eval(zeta)); - challenger.observe_extension_element::(&poly1.to_extension::().eval(zeta)); - challenger.observe_extension_element::(&poly2.to_extension::().eval(zeta)); - let mut verifier_challenger = challenger.clone(); - - let _alpha = challenger.get_extension_challenge::(); - - let composition_poly = poly0.mul_extension::(>::Extension::ONE); - let mut quotient = composition_poly.divide_by_linear(zeta); - quotient.coeffs.push(>::Extension::ZERO); - let lde_final_poly_0 = quotient.lde(fri_params.config.rate_bits); - let lde_final_values_0 = lde_final_poly_0.coset_fft(F::coset_shift().into()); - - let composition_poly = poly1.mul_extension::(>::Extension::ONE); - let mut quotient = composition_poly.divide_by_linear(zeta); - quotient.coeffs.push(>::Extension::ZERO); - let lde_final_poly_1 = quotient.lde(fri_params.config.rate_bits); - let lde_final_values_1 = lde_final_poly_1.coset_fft(F::coset_shift().into()); - - let composition_poly = poly2.mul_extension::(>::Extension::ONE); - let mut quotient = composition_poly.divide_by_linear(zeta); - quotient.coeffs.push(>::Extension::ZERO); - let lde_final_poly_2 = quotient.lde(fri_params.config.rate_bits); - let lde_final_values_2 = lde_final_poly_2.coset_fft(F::coset_shift().into()); - - let proof = batch_fri_proof::( - &[&trace_oracle.field_merkle_tree], - lde_final_poly_0, - &[lde_final_values_0, lde_final_values_1, lde_final_values_2], - &mut challenger, - &fri_params, - &mut timing, - ); - - let get_test_fri_instance = |polynomial_index: usize| -> FriInstanceInfo { - FriInstanceInfo { - oracles: vec![FriOracleInfo { - num_polys: 1, - blinding: false, - }], - batches: vec![FriBatchInfo { - point: zeta, - polynomials: vec![FriPolynomialInfo { - oracle_index: 0, - polynomial_index, - }], - }], - } - }; - let fri_instances = vec![ - get_test_fri_instance(0), - get_test_fri_instance(1), - get_test_fri_instance(2), - ]; - let fri_challenges = verifier_challenger.fri_challenges::( - &proof.commit_phase_merkle_caps, - &proof.final_poly, - proof.pow_witness, - k0, - &fri_params.config, - ); - let fri_opening_batch_0 = FriOpenings { - batches: vec![FriOpeningBatch { - values: vec![poly0.to_extension::().eval(zeta)], - }], - }; - let fri_opening_batch_1 = FriOpenings { - batches: vec![FriOpeningBatch { - values: vec![poly1.to_extension::().eval(zeta)], - }], - }; - let fri_opening_batch_2 = FriOpenings { - batches: vec![FriOpeningBatch { - values: vec![poly2.to_extension::().eval(zeta)], - }], - }; - let fri_openings = vec![ - fri_opening_batch_0, - fri_opening_batch_1, - fri_opening_batch_2, - ]; - - verify_batch_fri_proof::( - &[k0, k1, k2], - &fri_instances, - &fri_openings, - &fri_challenges, - &[trace_oracle.field_merkle_tree.cap], - &proof, - &fri_params, - ) - } -} diff --git a/plonky2/src/fri/batch_recursive_verifier.rs b/plonky2/src/fri/batch_recursive_verifier.rs deleted file mode 100644 index 90e7259fc9..0000000000 --- a/plonky2/src/fri/batch_recursive_verifier.rs +++ /dev/null @@ -1,332 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::{format, vec::Vec}; - -use itertools::Itertools; - -use crate::field::extension::Extendable; -use crate::fri::proof::{ - FriChallengesTarget, FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, -}; -use crate::fri::recursive_verifier::PrecomputedReducedOpeningsTarget; -use crate::fri::structure::{FriBatchInfoTarget, FriInstanceInfoTarget, FriOpeningsTarget}; -use crate::fri::FriParams; -use crate::hash::hash_types::{MerkleCapTarget, RichField}; -use crate::iop::ext_target::{flatten_target, ExtensionTarget}; -use crate::iop::target::{BoolTarget, Target}; -use crate::plonk::circuit_builder::CircuitBuilder; -use crate::plonk::config::{AlgebraicHasher, GenericConfig}; -use crate::util::reducing::ReducingFactorTarget; -use crate::with_context; - -impl, const D: usize> CircuitBuilder { - pub fn verify_batch_fri_proof>( - &mut self, - degree_bits: &[usize], - instance: &[FriInstanceInfoTarget], - openings: &[FriOpeningsTarget], - challenges: &FriChallengesTarget, - initial_merkle_caps: &[MerkleCapTarget], - proof: &FriProofTarget, - params: &FriParams, - ) where - C::Hasher: AlgebraicHasher, - { - if let Some(max_arity_bits) = params.max_arity_bits() { - self.check_recursion_config(max_arity_bits); - } - - debug_assert_eq!( - params.final_poly_len(), - proof.final_poly.len(), - "Final polynomial has wrong degree." - ); - - with_context!( - self, - "check PoW", - self.fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config) - ); - - // Check that parameters are coherent. - debug_assert_eq!( - params.config.num_query_rounds, - proof.query_round_proofs.len(), - "Number of query rounds does not match config." - ); - - let mut precomputed_reduced_evals = Vec::with_capacity(openings.len()); - for opn in openings.iter() { - let pre = with_context!( - self, - "precompute reduced evaluations", - PrecomputedReducedOpeningsTarget::from_os_and_alpha( - opn, - challenges.fri_alpha, - self - ) - ); - precomputed_reduced_evals.push(pre); - } - let degree_bits = degree_bits - .iter() - .map(|d| d + params.config.rate_bits) - .collect_vec(); - - for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { - // To minimize noise in our logs, we will only record a context for a single FRI query. - // The very first query will have some extra gates due to constants being registered, so - // the second query is a better representative. - let level = if i == 1 { - log::Level::Debug - } else { - log::Level::Trace - }; - - let num_queries = proof.query_round_proofs.len(); - with_context!( - self, - level, - &format!("verify one (of {num_queries}) query rounds"), - self.batch_fri_verifier_query_round::( - °ree_bits, - instance, - challenges, - &precomputed_reduced_evals, - initial_merkle_caps, - proof, - challenges.fri_query_indices[i], - round_proof, - params, - ) - ); - } - } - - fn batch_fri_verify_initial_proof>( - &mut self, - degree_bits: &[usize], - instances: &[FriInstanceInfoTarget], - x_index_bits: &[BoolTarget], - proof: &FriInitialTreeProofTarget, - initial_merkle_caps: &[MerkleCapTarget], - cap_index: Target, - ) { - for (i, ((evals, merkle_proof), cap)) in proof - .evals_proofs - .iter() - .zip(initial_merkle_caps) - .enumerate() - { - let leaves = instances - .iter() - .scan(0, |leaf_index, inst| { - let num_polys = inst.oracles[i].num_polys; - let leaves = (*leaf_index..*leaf_index + num_polys) - .map(|idx| evals[idx]) - .collect::>(); - *leaf_index += num_polys; - Some(leaves) - }) - .collect::>(); - - with_context!( - self, - &format!("verify {i}'th initial Merkle proof"), - self.verify_field_merkle_proof_to_cap_with_cap_index::( - &leaves, - degree_bits, - x_index_bits, - cap_index, - cap, - merkle_proof - ) - ); - } - } - - fn batch_fri_combine_initial( - &mut self, - instance: &[FriInstanceInfoTarget], - index: usize, - proof: &FriInitialTreeProofTarget, - alpha: ExtensionTarget, - subgroup_x: Target, - precomputed_reduced_evals: &PrecomputedReducedOpeningsTarget, - params: &FriParams, - ) -> ExtensionTarget { - assert!(D > 1, "Not implemented for D=1."); - let degree_log = params.degree_bits; - debug_assert_eq!( - degree_log, - params.config.cap_height + proof.evals_proofs[0].1.siblings.len() - - params.config.rate_bits - ); - let subgroup_x = self.convert_to_ext(subgroup_x); - let mut alpha = ReducingFactorTarget::new(alpha); - let mut sum = self.zero_extension(); - - for (batch, reduced_openings) in instance[index] - .batches - .iter() - .zip(&precomputed_reduced_evals.reduced_openings_at_point) - { - let FriBatchInfoTarget { point, polynomials } = batch; - let evals = polynomials - .iter() - .map(|p| { - let poly_blinding = instance[index].oracles[p.oracle_index].blinding; - let salted = params.hiding && poly_blinding; - proof.unsalted_eval(p.oracle_index, p.polynomial_index, salted) - }) - .collect_vec(); - let reduced_evals = alpha.reduce_base(&evals, self); - let numerator = self.sub_extension(reduced_evals, *reduced_openings); - let denominator = self.sub_extension(subgroup_x, *point); - sum = alpha.shift(sum, self); - sum = self.div_add_extension(numerator, denominator, sum); - } - - sum - } - - fn batch_fri_verifier_query_round>( - &mut self, - degree_bits: &[usize], - instance: &[FriInstanceInfoTarget], - challenges: &FriChallengesTarget, - precomputed_reduced_evals: &[PrecomputedReducedOpeningsTarget], - initial_merkle_caps: &[MerkleCapTarget], - proof: &FriProofTarget, - x_index: Target, - round_proof: &FriQueryRoundTarget, - params: &FriParams, - ) where - C::Hasher: AlgebraicHasher, - { - let mut n = degree_bits[0]; - - // Note that this `low_bits` decomposition permits non-canonical binary encodings. Here we - // verify that this has a negligible impact on soundness error. - Self::assert_noncanonical_indices_ok(¶ms.config); - let mut x_index_bits = self.low_bits(x_index, n, F::BITS); - - let cap_index = - self.le_sum(x_index_bits[x_index_bits.len() - params.config.cap_height..].iter()); - with_context!( - self, - "check FRI initial proof", - self.batch_fri_verify_initial_proof::( - degree_bits, - instance, - &x_index_bits, - &round_proof.initial_trees_proof, - initial_merkle_caps, - cap_index - ) - ); - - // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let mut subgroup_x = with_context!(self, "compute x from its index", { - let g = self.constant(F::coset_shift()); - let phi = F::primitive_root_of_unity(n); - let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); - self.mul(g, phi) - }); - - let mut batch_index = 0; - - // old_eval is the last derived evaluation; it will be checked for consistency with its - // committed "parent" value in the next iteration. - let mut old_eval = with_context!( - self, - "combine initial oracles", - self.batch_fri_combine_initial( - instance, - batch_index, - &round_proof.initial_trees_proof, - challenges.fri_alpha, - subgroup_x, - &precomputed_reduced_evals[batch_index], - params, - ) - ); - batch_index += 1; - - for (i, &arity_bits) in params.reduction_arity_bits.iter().enumerate() { - let evals = &round_proof.steps[i].evals; - - // Split x_index into the index of the coset x is in, and the index of x within that coset. - let coset_index_bits = x_index_bits[arity_bits..].to_vec(); - let x_index_within_coset_bits = &x_index_bits[..arity_bits]; - let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); - - // Check consistency with our old evaluation from the previous round. - let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); - self.connect_extension(new_eval, old_eval); - - // Infer P(y) from {P(x)}_{x^arity=y}. - old_eval = with_context!( - self, - "infer evaluation using interpolation", - self.compute_evaluation( - subgroup_x, - x_index_within_coset_bits, - arity_bits, - evals, - challenges.fri_betas[i], - ) - ); - - with_context!( - self, - "verify FRI round Merkle proof.", - self.verify_merkle_proof_to_cap_with_cap_index::( - flatten_target(evals), - &coset_index_bits, - cap_index, - &proof.commit_phase_merkle_caps[i], - &round_proof.steps[i].merkle_proof, - ) - ); - - // Update the point x to x^arity. - subgroup_x = self.exp_power_of_2(subgroup_x, arity_bits); - - x_index_bits = coset_index_bits; - n -= arity_bits; - - if batch_index < degree_bits.len() && n == degree_bits[batch_index] { - let subgroup_x_init = with_context!(self, "compute init x from its index", { - let g = self.constant(F::coset_shift()); - let phi = F::primitive_root_of_unity(n); - let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); - self.mul(g, phi) - }); - let eval = self.batch_fri_combine_initial( - instance, - batch_index, - &round_proof.initial_trees_proof, - challenges.fri_alpha, - subgroup_x_init, - &precomputed_reduced_evals[batch_index], - params, - ); - old_eval = self.mul_extension(old_eval, challenges.fri_betas[i]); - old_eval = self.add_extension(old_eval, eval); - batch_index += 1; - } - } - - // Final check of FRI. After all the reductions, we check that the final polynomial is equal - // to the one sent by the prover. - let eval = with_context!( - self, - &format!( - "evaluate final polynomial of length {}", - proof.final_poly.len() - ), - proof.final_poly.eval_scalar(self, subgroup_x) - ); - self.connect_extension(eval, old_eval); - } -} diff --git a/plonky2/src/fri/batch_verifier.rs b/plonky2/src/fri/batch_verifier.rs deleted file mode 100644 index e978572d35..0000000000 --- a/plonky2/src/fri/batch_verifier.rs +++ /dev/null @@ -1,251 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -use anyhow::ensure; -use itertools::Itertools; -use plonky2_field::extension::{flatten, Extendable, FieldExtension}; -use plonky2_field::types::Field; - -use crate::fri::proof::{FriChallenges, FriInitialTreeProof, FriProof, FriQueryRound}; -use crate::fri::structure::{FriBatchInfo, FriInstanceInfo, FriOpenings}; -use crate::fri::validate_shape::validate_batch_fri_proof_shape; -use crate::fri::verifier::{ - compute_evaluation, fri_verify_proof_of_work, PrecomputedReducedOpenings, -}; -use crate::fri::FriParams; -use crate::hash::hash_types::RichField; -use crate::hash::merkle_proofs::{verify_field_merkle_proof_to_cap, verify_merkle_proof_to_cap}; -use crate::hash::merkle_tree::MerkleCap; -use crate::plonk::config::{GenericConfig, Hasher}; -use crate::util::reducing::ReducingFactor; -use crate::util::reverse_bits; - -pub fn verify_batch_fri_proof< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - degree_bits: &[usize], - instances: &[FriInstanceInfo], - openings: &[FriOpenings], - challenges: &FriChallenges, - initial_merkle_cap: &[MerkleCap], - proof: &FriProof, - params: &FriParams, -) -> anyhow::Result<()> { - validate_batch_fri_proof_shape::(proof, instances, params)?; - - // Check PoW. - fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config)?; - - // Check that parameters are coherent. - ensure!( - params.config.num_query_rounds == proof.query_round_proofs.len(), - "Number of query rounds does not match config." - ); - - let mut precomputed_reduced_evals = Vec::with_capacity(openings.len()); - for opn in openings.iter() { - let pre = PrecomputedReducedOpenings::from_os_and_alpha(opn, challenges.fri_alpha); - precomputed_reduced_evals.push(pre); - } - let degree_bits = degree_bits - .iter() - .map(|d| d + params.config.rate_bits) - .collect_vec(); - for (&x_index, round_proof) in challenges - .fri_query_indices - .iter() - .zip(&proof.query_round_proofs) - { - batch_fri_verifier_query_round::( - °ree_bits, - instances, - challenges, - &precomputed_reduced_evals, - initial_merkle_cap, - proof, - x_index, - round_proof, - params, - )?; - } - - Ok(()) -} - -fn batch_fri_verify_initial_proof, H: Hasher, const D: usize>( - degree_bits: &[usize], - instances: &[FriInstanceInfo], - x_index: usize, - proof: &FriInitialTreeProof, - initial_merkle_caps: &[MerkleCap], -) -> anyhow::Result<()> { - for (oracle_index, ((evals, merkle_proof), cap)) in proof - .evals_proofs - .iter() - .zip(initial_merkle_caps) - .enumerate() - { - let leaves = instances - .iter() - .scan(0, |leaf_index, inst| { - let num_polys = inst.oracles[oracle_index].num_polys; - let leaves = (*leaf_index..*leaf_index + num_polys) - .map(|idx| evals[idx]) - .collect::>(); - *leaf_index += num_polys; - Some(leaves) - }) - .collect::>(); - - verify_field_merkle_proof_to_cap::(&leaves, degree_bits, x_index, cap, merkle_proof)?; - } - - Ok(()) -} - -fn batch_fri_combine_initial< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - instances: &[FriInstanceInfo], - index: usize, - proof: &FriInitialTreeProof, - alpha: F::Extension, - subgroup_x: F, - precomputed_reduced_evals: &PrecomputedReducedOpenings, - params: &FriParams, -) -> F::Extension { - assert!(D > 1, "Not implemented for D=1."); - let subgroup_x = F::Extension::from_basefield(subgroup_x); - let mut alpha = ReducingFactor::new(alpha); - let mut sum = F::Extension::ZERO; - - for (batch, reduced_openings) in instances[index] - .batches - .iter() - .zip(&precomputed_reduced_evals.reduced_openings_at_point) - { - let FriBatchInfo { point, polynomials } = batch; - let evals = polynomials - .iter() - .map(|p| { - let poly_blinding = instances[index].oracles[p.oracle_index].blinding; - let salted = params.hiding && poly_blinding; - proof.unsalted_eval(p.oracle_index, p.polynomial_index, salted) - }) - .map(F::Extension::from_basefield); - let reduced_evals = alpha.reduce(evals); - let numerator = reduced_evals - *reduced_openings; - let denominator = subgroup_x - *point; - sum = alpha.shift(sum); - sum += numerator / denominator; - } - - sum -} - -fn batch_fri_verifier_query_round< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - degree_bits: &[usize], - instances: &[FriInstanceInfo], - challenges: &FriChallenges, - precomputed_reduced_evals: &[PrecomputedReducedOpenings], - initial_merkle_caps: &[MerkleCap], - proof: &FriProof, - mut x_index: usize, - round_proof: &FriQueryRound, - params: &FriParams, -) -> anyhow::Result<()> { - batch_fri_verify_initial_proof::( - degree_bits, - instances, - x_index, - &round_proof.initial_trees_proof, - initial_merkle_caps, - )?; - let mut n = degree_bits[0]; - // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR - * F::primitive_root_of_unity(n).exp_u64(reverse_bits(x_index, n) as u64); - - let mut batch_index = 0; - // old_eval is the last derived evaluation; it will be checked for consistency with its - // committed "parent" value in the next iteration. - let mut old_eval = batch_fri_combine_initial::( - instances, - batch_index, - &round_proof.initial_trees_proof, - challenges.fri_alpha, - subgroup_x, - &precomputed_reduced_evals[batch_index], - params, - ); - batch_index += 1; - - for (i, &arity_bits) in params.reduction_arity_bits.iter().enumerate() { - let arity = 1 << arity_bits; - let evals = &round_proof.steps[i].evals; - - // Split x_index into the index of the coset x is in, and the index of x within that coset. - let coset_index = x_index >> arity_bits; - let x_index_within_coset = x_index & (arity - 1); - - // Check consistency with our old evaluation from the previous round. - ensure!(evals[x_index_within_coset] == old_eval); - - old_eval = compute_evaluation( - subgroup_x, - x_index_within_coset, - arity_bits, - evals, - challenges.fri_betas[i], - ); - verify_merkle_proof_to_cap::( - flatten(evals), - coset_index, - &proof.commit_phase_merkle_caps[i], - &round_proof.steps[i].merkle_proof, - )?; - - // Update the point x to x^arity. - subgroup_x = subgroup_x.exp_power_of_2(arity_bits); - x_index = coset_index; - n -= arity_bits; - - if batch_index < degree_bits.len() && n == degree_bits[batch_index] { - let subgroup_x_init = F::MULTIPLICATIVE_GROUP_GENERATOR - * F::primitive_root_of_unity(n).exp_u64(reverse_bits(x_index, n) as u64); - let eval = batch_fri_combine_initial::( - instances, - batch_index, - &round_proof.initial_trees_proof, - challenges.fri_alpha, - subgroup_x_init, - &precomputed_reduced_evals[batch_index], - params, - ); - old_eval = old_eval * challenges.fri_betas[i] + eval; - batch_index += 1; - } - } - assert_eq!( - batch_index, - instances.len(), - "Wrong number of folded instances." - ); - - // 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.into()) == old_eval, - "Final polynomial evaluation is invalid." - ); - - Ok(()) -} diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index c99cbf21b6..5f18600c3c 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -10,10 +10,6 @@ use serde::Serialize; use crate::fri::reduction_strategies::FriReductionStrategy; -pub mod batch_oracle; -pub mod batch_prover; -pub mod batch_recursive_verifier; -pub mod batch_verifier; mod challenges; pub mod oracle; pub mod proof; diff --git a/plonky2/src/hash/field_merkle_tree.rs b/plonky2/src/hash/field_merkle_tree.rs deleted file mode 100644 index 6e1828c2da..0000000000 --- a/plonky2/src/hash/field_merkle_tree.rs +++ /dev/null @@ -1,337 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::vec; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -use itertools::Itertools; - -use crate::hash::hash_types::{RichField, NUM_HASH_OUT_ELTS}; -use crate::hash::merkle_proofs::MerkleProof; -use crate::hash::merkle_tree::{ - capacity_up_to_mut, fill_digests_buf, merkle_tree_prove, MerkleCap, -}; -use crate::plonk::config::{GenericHashOut, Hasher}; -use crate::util::log2_strict; - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct FieldMerkleTree> { - /// The data stored in the Merkle tree leaves. - pub leaves: Vec>>, - - /// Merkle tree node hashes, analogous to `digests` in `MerkleTree`. - pub digests: Vec, - - /// Represents the roots of the Merkle tree. This allows for using any layer as the root of the tree. - pub cap: MerkleCap, - - /// Represents the heights at which leaves reside within the tree. - pub leaf_heights: Vec, -} - -impl> FieldMerkleTree { - /// Each element in the `leaves` vector represents a matrix (a vector of vectors). - /// The height of each matrix should be a power of two. - /// The `leaves` vector should be sorted by matrix height, from tallest to shortest, with no duplicate heights. - // TODO: FieldMerkleTree does not handle duplicates; this is deferred to the caller. Revisit when implementing batch FRI to potentially optimize. - pub fn new(mut leaves: Vec>>, cap_height: usize) -> Self { - assert!(!leaves.is_empty()); - assert!(leaves.iter().all(|leaf| leaf.len().is_power_of_two())); - assert!(leaves - .windows(2) - .all(|pair| { pair[0].len() > pair[1].len() })); - - let leaves_len = leaves[0].len(); - let log2_leaves_len = log2_strict(leaves_len); - assert!( - cap_height <= log2_leaves_len, - "cap_height={} should be at most log2(leaves.len())={}", - cap_height, - log2_leaves_len - ); - - let mut leaf_heights = vec![]; - - let num_digests = 2 * (leaves_len - (1 << cap_height)); - let mut digests = Vec::with_capacity(num_digests); - let digests_buf = capacity_up_to_mut(&mut digests, num_digests); - let mut digests_buf_pos = 0; - - let mut cap = vec![]; - let dummy_leaves = vec![vec![F::ZERO]; 1 << cap_height]; - leaves.push(dummy_leaves); - for window in leaves.windows(2) { - let cur = &window[0]; - let next = &window[1]; - - let cur_leaf_len = cur.len(); - let next_cap_len = next.len(); - let next_cap_height = log2_strict(next_cap_len); - - leaf_heights.push(log2_strict(cur_leaf_len)); - - let num_tmp_digests = 2 * (cur_leaf_len - next_cap_len); - - if cur_leaf_len == leaves_len { - // The bottom leaf layer - cap = Vec::with_capacity(next_cap_len); - let tmp_cap_buf = capacity_up_to_mut(&mut cap, next_cap_len); - fill_digests_buf::( - &mut digests_buf[digests_buf_pos..(digests_buf_pos + num_tmp_digests)], - tmp_cap_buf, - &cur[..], - next_cap_height, - ); - } else { - // The rest leaf layers - let new_leaves: Vec> = cap - .iter() - .enumerate() - .map(|(i, cap_hash)| { - let mut new_hash = Vec::with_capacity(NUM_HASH_OUT_ELTS + cur[i].len()); - new_hash.extend(&cap_hash.to_vec()); - new_hash.extend(&cur[i]); - new_hash - }) - .collect(); - cap.clear(); - cap.reserve_exact(next_cap_len); - let tmp_cap_buf = capacity_up_to_mut(&mut cap, next_cap_len); - fill_digests_buf::( - &mut digests_buf[digests_buf_pos..(digests_buf_pos + num_tmp_digests)], - tmp_cap_buf, - &new_leaves[..], - next_cap_height, - ); - } - - unsafe { - cap.set_len(next_cap_len); - } - - digests_buf_pos += num_tmp_digests; - } - - unsafe { - // SAFETY: `fill_digests_buf` and `cap` initialized the spare capacity up to - // `num_digests` and `len_cap`, resp. - digests.set_len(num_digests); - } - - // remove dummy leaves - leaves.pop(); - - Self { - leaves, - digests, - cap: MerkleCap(cap), - leaf_heights, - } - } - - /// Create a Merkle proof from a leaf index. - pub fn open_batch(&self, leaf_index: usize) -> MerkleProof { - let mut digests_buf_pos = 0; - let leaves_cap_height = log2_strict(self.leaves[0].len()); - let mut siblings = vec![]; - let mut cap_heights = self.leaf_heights.clone(); - cap_heights.push(log2_strict(self.cap.len())); - for window in cap_heights.windows(2) { - let cur_cap_height = window[0]; - let next_cap_height = window[1]; - let num_digests: usize = 2 * ((1 << cur_cap_height) - (1 << next_cap_height)); - siblings.extend::>(merkle_tree_prove::( - leaf_index >> (leaves_cap_height - cur_cap_height), - 1 << cur_cap_height, - next_cap_height, - &self.digests[digests_buf_pos..digests_buf_pos + num_digests], - )); - digests_buf_pos += num_digests; - } - - MerkleProof { siblings } - } - - pub fn values(&self, leaf_index: usize) -> Vec> { - let leaves_cap_height = log2_strict(self.leaves[0].len()); - self.leaves - .iter() - .zip(&self.leaf_heights) - .map(|(leaves, cap_height)| { - leaves[leaf_index >> (leaves_cap_height - cap_height)].clone() - }) - .collect_vec() - } -} - -#[cfg(test)] -mod tests { - #[cfg(not(feature = "std"))] - use alloc::vec; - - use anyhow::Result; - use plonky2_field::goldilocks_field::GoldilocksField; - use plonky2_field::types::Field; - - use super::*; - use crate::hash::merkle_proofs::verify_field_merkle_proof_to_cap; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type H = >::Hasher; - - #[test] - fn commit_single() -> Result<()> { - // mat_1 = [ - // 0 1 - // 2 1 - // 2 2 - // 0 0 - // ] - let mat_1 = vec![ - vec![F::ZERO, F::ONE], - vec![F::TWO, F::ONE], - vec![F::TWO, F::TWO], - vec![F::ZERO, F::ZERO], - ]; - let fmt: FieldMerkleTree = FieldMerkleTree::new(vec![mat_1], 0); - - let mat_1_leaf_hashes = [ - H::hash_or_noop(&[F::ZERO, F::ONE]), - H::hash_or_noop(&[F::TWO, F::ONE]), - H::hash_or_noop(&[F::TWO, F::TWO]), - H::hash_or_noop(&[F::ZERO, F::ZERO]), - ]; - assert_eq!(mat_1_leaf_hashes[0..2], fmt.digests[0..2]); - assert_eq!(mat_1_leaf_hashes[2..4], fmt.digests[4..6]); - - let layer_1 = [ - H::two_to_one(mat_1_leaf_hashes[0], mat_1_leaf_hashes[1]), - H::two_to_one(mat_1_leaf_hashes[2], mat_1_leaf_hashes[3]), - ]; - assert_eq!(layer_1, fmt.digests[2..4]); - - let root = H::two_to_one(layer_1[0], layer_1[1]); - assert_eq!(fmt.cap.flatten(), root.to_vec()); - - let proof = fmt.open_batch(2); - assert_eq!(proof.siblings, [mat_1_leaf_hashes[3], layer_1[0]]); - - let opened_values = fmt.values(2); - assert_eq!(opened_values, [vec![F::TWO, F::TWO]]); - - verify_field_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 2, &fmt.cap, &proof)?; - Ok(()) - } - - #[test] - fn commit_mixed() -> Result<()> { - // mat_1 = [ - // 0 1 - // 2 1 - // 2 2 - // 0 0 - // ] - let mat_1 = vec![ - vec![F::ZERO, F::ONE], - vec![F::TWO, F::ONE], - vec![F::TWO, F::TWO], - vec![F::ZERO, F::ZERO], - ]; - - // mat_2 = [ - // 1 2 1 - // 0 2 2 - // ] - let mat_2 = vec![vec![F::ONE, F::TWO, F::ONE], vec![F::ZERO, F::TWO, F::TWO]]; - - let fmt: FieldMerkleTree = - FieldMerkleTree::new(vec![mat_1, mat_2.clone()], 0); - - let mat_1_leaf_hashes = [ - H::hash_or_noop(&[F::ZERO, F::ONE]), - H::hash_or_noop(&[F::TWO, F::ONE]), - H::hash_or_noop(&[F::TWO, F::TWO]), - H::hash_or_noop(&[F::ZERO, F::ZERO]), - ]; - assert_eq!(mat_1_leaf_hashes, fmt.digests[0..4]); - - let hidden_layer = [ - H::two_to_one(mat_1_leaf_hashes[0], mat_1_leaf_hashes[1]).to_vec(), - H::two_to_one(mat_1_leaf_hashes[2], mat_1_leaf_hashes[3]).to_vec(), - ]; - let new_leaves = hidden_layer - .iter() - .zip(mat_2.iter()) - .map(|(row1, row2)| { - let mut new_row = row1.clone(); - new_row.extend_from_slice(row2); - new_row - }) - .collect::>>(); - let layer_1 = [ - H::hash_or_noop(&new_leaves[0]), - H::hash_or_noop(&new_leaves[1]), - ]; - assert_eq!(layer_1, fmt.digests[4..]); - - let root = H::two_to_one(layer_1[0], layer_1[1]); - assert_eq!(fmt.cap.flatten(), root.to_vec()); - - let proof = fmt.open_batch(1); - assert_eq!(proof.siblings, [mat_1_leaf_hashes[0], layer_1[1]]); - - let opened_values = fmt.values(1); - assert_eq!( - opened_values, - [vec![F::TWO, F::ONE], vec![F::ONE, F::TWO, F::ONE]] - ); - - verify_field_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 1, &fmt.cap, &proof)?; - Ok(()) - } - - #[test] - fn test_field_merkle_trees() -> Result<()> { - let leaves_1 = crate::hash::merkle_tree::tests::random_data::(1024, 7); - let leaves_2 = crate::hash::merkle_tree::tests::random_data::(64, 3); - let leaves_3 = crate::hash::merkle_tree::tests::random_data::(32, 100); - - let fmt: FieldMerkleTree = - FieldMerkleTree::new(vec![leaves_1, leaves_2, leaves_3], 3); - for index in [0, 1023, 512, 255] { - let proof = fmt.open_batch(index); - let opened_values = fmt.values(index); - verify_field_merkle_proof_to_cap( - &opened_values, - &fmt.leaf_heights, - index, - &fmt.cap, - &proof, - )?; - } - - Ok(()) - } - - #[test] - fn test_field_merkle_trees_cap_at_leaves_height() -> Result<()> { - let leaves_1 = crate::hash::merkle_tree::tests::random_data::(16, 7); - - let fmt: FieldMerkleTree = FieldMerkleTree::new(vec![leaves_1], 4); - for index in 0..16 { - let proof = fmt.open_batch(index); - let opened_values = fmt.values(index); - verify_field_merkle_proof_to_cap( - &opened_values, - &fmt.leaf_heights, - index, - &fmt.cap, - &proof, - )?; - } - - Ok(()) - } -}