From deb981e97b44a0538097aeff0a93acce35767d30 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 27 Apr 2021 09:21:04 +0200 Subject: [PATCH] More fixes --- src/fri.rs | 168 ++++++++++++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 79 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index 85843c44..281fa850 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -218,8 +218,8 @@ fn fri_query_round( let merkle_proof = tree.prove_subtree(x_index & (next_domain_size - 1), arity_bits); query_steps.push(FriQueryStep { - merkle_proof, evals, + merkle_proof, }); domain_size = next_domain_size; @@ -294,89 +294,99 @@ fn verify_fri_proof( "Number of reductions should be non-zero." ); - for round in 0..config.num_query_rounds { - let round_proof = &proof.query_round_proofs[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; - // `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); - 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(); - evaluations.push(evals); - } else { - let last_evals = &evaluations[i - 1]; - // Infer P(y) from {P(x)}_{x^arity=y}. - let e_x = compute_evaluation( - subgroup_x, - 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); - 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), - proof.commit_phase_merkle_roots[i], - &round_proof.steps[i].merkle_proof, - true, - )?; - - if i > 0 { - // Update the point x to x^arity. - for _ in 0..config.reduction_arity_bits[i - 1] { - subgroup_x = subgroup_x.square(); - } - } - domain_size = next_domain_size; - } - - let last_evals = evaluations.last().unwrap(); - let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); - let purported_eval = compute_evaluation( - subgroup_x, - final_arity_bits, - last_evals, - *betas.last().unwrap(), - ); - for _ in 0..final_arity_bits { - subgroup_x = subgroup_x.square(); - } - - // 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) == purported_eval, - "Final polynomial evaluation is invalid." - ); + for round_proof in &proof.query_round_proofs { + fri_verifier_query_round(&proof, challenger, n, &betas, round_proof, config)?; } Ok(()) } +fn fri_verifier_query_round( + proof: &FriProof, + challenger: &mut Challenger, + n: usize, + betas: &[F], + round_proof: &FriQueryRound, + config: &FriConfig, +) -> Result<()> { + 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; + // `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); + 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(); + evaluations.push(evals); + } else { + let last_evals = &evaluations[i - 1]; + // Infer P(y) from {P(x)}_{x^arity=y}. + let e_x = compute_evaluation( + subgroup_x, + 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); + 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), + proof.commit_phase_merkle_roots[i], + &round_proof.steps[i].merkle_proof, + true, + )?; + + if i > 0 { + // Update the point x to x^arity. + for _ in 0..config.reduction_arity_bits[i - 1] { + subgroup_x = subgroup_x.square(); + } + } + domain_size = next_domain_size; + } + + let last_evals = evaluations.last().unwrap(); + let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); + let purported_eval = compute_evaluation( + subgroup_x, + final_arity_bits, + last_evals, + *betas.last().unwrap(), + ); + for _ in 0..final_arity_bits { + subgroup_x = subgroup_x.square(); + } + + // 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) == purported_eval, + "Final polynomial evaluation is invalid." + ); + + Ok(()) +} + #[cfg(test)] mod tests { use super::*;