From 79a8ccd9a09b4b04212c8ea450701c5f741c23b9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 28 Apr 2021 18:38:05 +0200 Subject: [PATCH 1/4] Working bit-reversed version --- src/field/crandall_field.rs | 4 +- src/fri.rs | 142 ++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 75 deletions(-) diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 1ff8d9d4..91b230af 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -38,13 +38,13 @@ impl Hash for CrandallField { impl Display for CrandallField { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) + Display::fmt(&self.to_canonical_u64(), f) } } impl Debug for CrandallField { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(&self.0, f) + Debug::fmt(&self.to_canonical_u64(), f) } } diff --git a/src/fri.rs b/src/fri.rs index 281fa850..ce4178d3 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -2,13 +2,13 @@ use crate::field::fft::fft; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolate}; use crate::hash::hash_n_to_1; -use crate::merkle_proofs::verify_merkle_proof_subtree; +use crate::merkle_proofs::{verify_merkle_proof, verify_merkle_proof_subtree}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriQueryRound, FriQueryStep, Hash}; -use crate::util::{log2_strict, reverse_bits}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; use anyhow::{ensure, Result}; /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, @@ -93,18 +93,28 @@ fn fri_committed_trees( challenger: &mut Challenger, config: &FriConfig, ) -> (Vec>, PolynomialCoeffs) { - let mut trees = vec![MerkleTree::new( - polynomial_values.values.iter().map(|&v| vec![v]).collect(), - true, - )]; + let mut values = polynomial_values.clone(); let mut coeffs = polynomial_coeffs.clone(); - let mut values; - challenger.observe_hash(&trees[0].root); + let mut trees = Vec::new(); let num_reductions = config.reduction_arity_bits.len(); for i in 0..num_reductions { let arity = 1 << config.reduction_arity_bits[i]; + + reverse_index_bits_in_place(&mut values.values); + let tree = MerkleTree::new( + values + .values + .chunks(arity) + .map(|chunk| chunk.to_vec()) + .collect(), + false, + ); + + challenger.observe_hash(&tree.root); + trees.push(tree); + let beta = challenger.get_challenge(); // P(x) = sum_{i( .map(|chunk| reduce_with_powers(chunk, beta)) .collect::>(), ); - if i == num_reductions - 1 { - // We don't need a Merkle root for the final polynomial, since we send its - // coefficients directly to the verifier. - break; - } values = fft(coeffs.clone()); - - let tree = MerkleTree::new(values.values.iter().map(|&v| vec![v]).collect(), true); - challenger.observe_hash(&tree.root); - trees.push(tree); } + challenger.observe_elements(&coeffs.coeffs); (trees, coeffs) } @@ -180,11 +182,11 @@ fn fri_prover_query_rounds( config: &FriConfig, ) -> Vec> { (0..config.num_query_rounds) - .map(|_| fri_query_round(trees, challenger, n, config)) + .map(|_| fri_prover_query_round(trees, challenger, n, config)) .collect() } -fn fri_query_round( +fn fri_prover_query_round( trees: &[MerkleTree], challenger: &mut Challenger, n: usize, @@ -200,22 +202,17 @@ fn fri_query_round( let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; x_index %= domain_size; - let roots_coset_indices = coset_indices(x_index, next_domain_size, domain_size, arity); let evals = if i == 0 { // For the first layer, we need to send the evaluation at `x` too. - roots_coset_indices - .iter() - .map(|&index| tree.get(index)[0]) - .collect() + tree.get(x_index >> arity_bits).to_vec() } else { // For the other layers, we don't need to send the evaluation at `x`, since it can // be inferred by the verifier. See the `compute_evaluation` function. - roots_coset_indices[1..] - .iter() - .map(|&index| tree.get(index)[0]) - .collect() + let mut evals = tree.get(x_index >> arity_bits).to_vec(); + evals.remove(x_index & (arity - 1)); + evals }; - let merkle_proof = tree.prove_subtree(x_index & (next_domain_size - 1), arity_bits); + let merkle_proof = tree.prove(x_index >> arity_bits); query_steps.push(FriQueryStep { evals, @@ -223,32 +220,34 @@ fn fri_query_round( }); domain_size = next_domain_size; + x_index >>= arity_bits; } FriQueryRound { steps: query_steps } } -/// Returns the indices in the domain of all `y` in `F` with `y^arity=x^arity`, starting with `x` itself. -fn coset_indices( - x_index: usize, - next_domain_size: usize, - domain_size: usize, - arity: usize, -) -> Vec { - (0..arity) - .map(|i| (i * next_domain_size + x_index) % domain_size) - .collect() -} - /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. -fn compute_evaluation(x: F, arity_bits: usize, last_evals: &[F], beta: F) -> F { +fn compute_evaluation( + x: F, + old_x_index: usize, + arity_bits: usize, + last_evals: &[F], + beta: F, +) -> F { debug_assert_eq!(last_evals.len(), 1 << arity_bits); - // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. + let g = F::primitive_root_of_unity(arity_bits); + + // The evaluation vector needs to be reordered first. + let mut evals = last_evals.to_vec(); + reverse_index_bits_in_place(&mut evals); + evals.rotate_left(reverse_bits(old_x_index, arity_bits)); + + // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. let points = g .powers() - .zip(last_evals) - .map(|(y, &e)| (x * y, e)) + .zip(evals.into_iter()) + .map(|(y, e)| (x * y, e)) .collect::>(); let barycentric_weights = barycentric_weights(&points); interpolate(&points, beta, &barycentric_weights) @@ -313,11 +312,13 @@ fn fri_verifier_query_round( let x = challenger.get_challenge(); let mut domain_size = n; let mut x_index = x.to_canonical_u64() as usize; + let mut old_x_index = 0; // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let mut subgroup_x = F::primitive_root_of_unity(log2_strict(n)).exp_usize(x_index % n); + let log_n = log2_strict(n); + let mut subgroup_x = + F::primitive_root_of_unity(log_n).exp_usize(reverse_bits(x_index % n, log_n)); for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { let arity = 1 << arity_bits; - x_index %= domain_size; let next_domain_size = domain_size >> arity_bits; if i == 0 { let evals = round_proof.steps[0].evals.clone(); @@ -327,33 +328,22 @@ fn fri_verifier_query_round( // Infer P(y) from {P(x)}_{x^arity=y}. let e_x = compute_evaluation( subgroup_x, + old_x_index, config.reduction_arity_bits[i - 1], last_evals, betas[i - 1], ); let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. - evals.insert(0, e_x); + evals.insert(x_index & (arity - 1), e_x); evaluations.push(evals); }; - let sorted_evals = { - let roots_coset_indices = coset_indices(x_index, next_domain_size, domain_size, arity); - let mut sorted_evals_enumerate = evaluations[i].iter().enumerate().collect::>(); - // We need to sort the evaluations so that they match their order in the Merkle tree. - sorted_evals_enumerate.sort_by_key(|&(j, _)| { - reverse_bits(roots_coset_indices[j], log2_strict(domain_size)) - }); - sorted_evals_enumerate - .into_iter() - .map(|(_, &e)| vec![e]) - .collect() - }; - verify_merkle_proof_subtree( - sorted_evals, - x_index & (next_domain_size - 1), + verify_merkle_proof( + evaluations[i].clone(), + x_index >> arity_bits, proof.commit_phase_merkle_roots[i], &round_proof.steps[i].merkle_proof, - true, + false, )?; if i > 0 { @@ -363,12 +353,15 @@ fn fri_verifier_query_round( } } domain_size = next_domain_size; + old_x_index = x_index; + x_index >>= arity_bits; } let last_evals = evaluations.last().unwrap(); let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); let purported_eval = compute_evaluation( subgroup_x, + old_x_index, final_arity_bits, last_evals, *betas.last().unwrap(), @@ -393,6 +386,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::fft::ifft; use anyhow::Result; + use rand::rngs::ThreadRng; use rand::Rng; fn test_fri( @@ -421,8 +415,7 @@ mod tests { Ok(()) } - fn gen_arities(degree_log: usize) -> Vec { - let mut rng = rand::thread_rng(); + fn gen_arities(degree_log: usize, rng: &mut ThreadRng) -> Vec { let mut arities = Vec::new(); let mut remaining = degree_log; while remaining > 0 { @@ -435,15 +428,18 @@ mod tests { #[test] fn test_fri_multi_params() -> Result<()> { + let mut rng = rand::thread_rng(); for degree_log in 1..6 { - for rate_bits in 0..4 { + for rate_bits in 0..3 { for num_query_round in 0..4 { - test_fri( - degree_log, - rate_bits, - gen_arities(degree_log), - num_query_round, - )?; + for _ in 0..3 { + test_fri( + degree_log, + rate_bits, + gen_arities(degree_log, &mut rng), + num_query_round, + )?; + } } } } From f624415a3c6c1066a35f030c48b6f33b6498abeb Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 28 Apr 2021 18:41:59 +0200 Subject: [PATCH 2/4] Clippy --- src/fri.rs | 2 +- src/merkle_proofs.rs | 28 ------------------ src/merkle_tree.rs | 67 +------------------------------------------- 3 files changed, 2 insertions(+), 95 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index ce4178d3..02d508a3 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -2,7 +2,7 @@ use crate::field::fft::fft; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolate}; use crate::hash::hash_n_to_1; -use crate::merkle_proofs::{verify_merkle_proof, verify_merkle_proof_subtree}; +use crate::merkle_proofs::verify_merkle_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 6209d9ab..8434ae49 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -48,34 +48,6 @@ pub(crate) fn verify_merkle_proof( Ok(()) } -/// Verifies that the given subtree is present at the given index in the Merkle tree with the -/// given root. -pub(crate) fn verify_merkle_proof_subtree( - subtree_leaves_data: Vec>, - subtree_index: usize, - merkle_root: Hash, - proof: &MerkleProof, - reverse_bits: bool, -) -> Result<()> { - let index = if reverse_bits { - crate::util::reverse_bits(subtree_index, proof.siblings.len()) - } else { - subtree_index - }; - let mut current_digest = MerkleTree::new(subtree_leaves_data, false).root; - for (i, &sibling_digest) in proof.siblings.iter().enumerate() { - let bit = (index >> i & 1) == 1; - current_digest = if bit { - compress(sibling_digest, current_digest) - } else { - compress(current_digest, sibling_digest) - } - } - ensure!(current_digest == merkle_root, "Invalid Merkle proof."); - - Ok(()) -} - impl CircuitBuilder { /// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// given root. diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index a74415a7..e7d535e0 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -78,42 +78,6 @@ impl MerkleTree { .collect(), } } - - /// Create a Merkle proof for an entire subtree. - /// Example: - /// ```tree - /// G - /// / \ - /// / \ - /// / \ - /// E F - /// / \ / \ - /// A B C D - /// ``` - /// `self.prove_subtree(0, 1)` gives a Merkle proof for the subtree E->(A,B), i.e., the - /// path (F,). - pub fn prove_subtree(&self, subtree_index: usize, subtree_height: usize) -> MerkleProof { - let index = if self.reverse_bits { - reverse_bits( - subtree_index, - log2_strict(self.leaves.len()) - subtree_height, - ) - } else { - subtree_index - }; - MerkleProof { - siblings: self - .layers - .iter() - .skip(subtree_height) - .scan(index, |acc, layer| { - let index = *acc ^ 1; - *acc >>= 1; - Some(layer[index]) - }) - .collect(), - } - } } #[cfg(test)] @@ -121,7 +85,7 @@ mod tests { use anyhow::Result; use crate::field::crandall_field::CrandallField; - use crate::merkle_proofs::{verify_merkle_proof, verify_merkle_proof_subtree}; + use crate::merkle_proofs::verify_merkle_proof; use super::*; @@ -143,32 +107,6 @@ mod tests { } Ok(()) } - fn verify_all_subtrees( - leaves: Vec>, - n: usize, - log_n: usize, - reverse_bits: bool, - ) -> Result<()> { - let tree = MerkleTree::new(leaves.clone(), reverse_bits); - for height in 0..=log_n { - for i in 0..(n >> height) { - let index = if reverse_bits { - crate::util::reverse_bits(i, log_n - height) - } else { - i - }; - let subtree_proof = tree.prove_subtree(i, height); - verify_merkle_proof_subtree( - tree.leaves[index << height..(index + 1) << height].to_vec(), - i, - tree.root, - &subtree_proof, - reverse_bits, - )?; - } - } - Ok(()) - } #[test] fn test_merkle_trees() -> Result<()> { @@ -179,10 +117,7 @@ mod tests { let leaves = random_data::(n, 7); verify_all_leaves(leaves.clone(), n, false)?; - verify_all_subtrees(leaves.clone(), n, log_n, false)?; - verify_all_leaves(leaves.clone(), n, true)?; - verify_all_subtrees(leaves, n, log_n, true)?; Ok(()) } From 8590407764348ad4fc84a1f864da7b9f7699f01c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 28 Apr 2021 22:55:16 +0200 Subject: [PATCH 3/4] Fixes based on PR comments --- src/fri.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index 02d508a3..b363f0b1 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -124,6 +124,7 @@ fn fri_committed_trees( .map(|chunk| reduce_with_powers(chunk, beta)) .collect::>(), ); + // TODO: Is it faster to interpolate? values = fft(coeffs.clone()); } @@ -196,12 +197,11 @@ fn fri_prover_query_round( // TODO: Challenger doesn't change between query rounds, so x is always the same. let x = challenger.get_challenge(); let mut domain_size = n; - let mut x_index = x.to_canonical_u64() as usize; + let mut x_index = x.to_canonical_u64() as usize % n; for (i, tree) in trees.iter().enumerate() { let arity_bits = config.reduction_arity_bits[i]; let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; - x_index %= domain_size; let evals = if i == 0 { // For the first layer, we need to send the evaluation at `x` too. tree.get(x_index >> arity_bits).to_vec() @@ -311,12 +311,11 @@ fn fri_verifier_query_round( let mut evaluations = Vec::new(); let x = challenger.get_challenge(); let mut domain_size = n; - let mut x_index = x.to_canonical_u64() as usize; + let mut x_index = x.to_canonical_u64() as usize % n; let mut old_x_index = 0; // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. let log_n = log2_strict(n); - let mut subgroup_x = - F::primitive_root_of_unity(log_n).exp_usize(reverse_bits(x_index % n, log_n)); + let mut subgroup_x = F::primitive_root_of_unity(log_n).exp_usize(reverse_bits(x_index, log_n)); for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; From fd3e8bcd4c7253928158fc457143d201bbd30898 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 29 Apr 2021 08:18:31 +0200 Subject: [PATCH 4/4] Minor fixes --- src/fri.rs | 2 +- src/merkle_proofs.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fri.rs b/src/fri.rs index b363f0b1..9376f449 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -246,7 +246,7 @@ fn compute_evaluation( // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. let points = g .powers() - .zip(evals.into_iter()) + .zip(evals) .map(|(y, e)| (x * y, e)) .collect::>(); let barycentric_weights = barycentric_weights(&points); diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 8434ae49..6a4cd033 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -29,6 +29,11 @@ pub(crate) fn verify_merkle_proof( proof: &MerkleProof, reverse_bits: bool, ) -> Result<()> { + ensure!( + leaf_index >> proof.siblings.len() == 0, + "Merkle leaf index is too large." + ); + let index = if reverse_bits { crate::util::reverse_bits(leaf_index, proof.siblings.len()) } else {