use rayon::prelude::*; use crate::field::extension_field::{flatten, unflatten, Extendable}; use crate::field::field_types::Field; use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; use crate::fri::FriConfig; use crate::hash::hash_types::HashOut; use crate::hash::hashing::hash_n_to_1; use crate::hash::merkle_tree::MerkleTree; use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::plonk_common::reduce_with_powers; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; use crate::util::reverse_index_bits_in_place; use crate::util::timing::TimingTree; /// Builds a FRI proof. pub fn fri_proof, const D: usize>( initial_merkle_trees: &[&MerkleTree], // Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero. lde_polynomial_coeffs: PolynomialCoeffs, // Evaluation of the polynomial on the large domain. lde_polynomial_values: PolynomialValues, challenger: &mut Challenger, config: &CircuitConfig, timing: &mut TimingTree, ) -> FriProof { let n = lde_polynomial_values.values.len(); assert_eq!(lde_polynomial_coeffs.coeffs.len(), n); // Commit phase let (trees, final_coeffs) = timed!( timing, "fold codewords in the commitment phase", fri_committed_trees( lde_polynomial_coeffs, lde_polynomial_values, challenger, config, ) ); // PoW phase let current_hash = challenger.get_hash(); let pow_witness = timed!( timing, "find for proof-of-work witness", fri_proof_of_work(current_hash, &config.fri_config) ); // Query phase let query_round_proofs = fri_prover_query_rounds( initial_merkle_trees, &trees, challenger, n, &config.fri_config, ); FriProof { commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(), query_round_proofs, final_poly: final_coeffs, pow_witness, } } fn fri_committed_trees, const D: usize>( mut coeffs: PolynomialCoeffs, mut values: PolynomialValues, challenger: &mut Challenger, config: &CircuitConfig, ) -> (Vec>, PolynomialCoeffs) { let mut trees = Vec::new(); let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR; let num_reductions = config.fri_config.reduction_arity_bits.len(); for i in 0..num_reductions { let arity = 1 << config.fri_config.reduction_arity_bits[i]; reverse_index_bits_in_place(&mut values.values); let chunked_values = values .values .par_chunks(arity) .map(|chunk: &[F::Extension]| flatten(chunk)) .collect(); let tree = MerkleTree::new(chunked_values, config.cap_height, false); challenger.observe_cap(&tree.cap); trees.push(tree); let beta = challenger.get_extension_challenge(); // P(x) = sum_{i>(), ); shift = shift.exp_u32(arity as u32); values = coeffs.coset_fft(shift.into()) } /// The coefficients being removed here should always be zero. coeffs.coeffs.truncate(coeffs.len() >> config.rate_bits); challenger.observe_extension_elements(&coeffs.coeffs); (trees, coeffs) } fn fri_proof_of_work(current_hash: HashOut, config: &FriConfig) -> F { (0..u64::MAX) .into_par_iter() .find_any(|&i| { hash_n_to_1( current_hash .elements .iter() .copied() .chain(Some(F::from_canonical_u64(i))) .collect(), false, ) .to_canonical_u64() .leading_zeros() >= config.proof_of_work_bits + (64 - F::order().bits()) as u32 }) .map(F::from_canonical_u64) .expect("Proof of work failed. This is highly unlikely!") } fn fri_prover_query_rounds, const D: usize>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, config: &FriConfig, ) -> Vec> { (0..config.num_query_rounds) .map(|_| fri_prover_query_round(initial_merkle_trees, trees, challenger, n, config)) .collect() } fn fri_prover_query_round, const D: usize>( initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, config: &FriConfig, ) -> FriQueryRound { let mut query_steps = Vec::new(); let x = challenger.get_challenge(); let mut x_index = x.to_canonical_u64() as usize % n; let initial_proof = initial_merkle_trees .iter() .map(|t| (t.get(x_index).to_vec(), t.prove(x_index))) .collect::>(); for (i, tree) in trees.iter().enumerate() { let arity_bits = config.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, } }