From 4c7ebdaddd92c0ab8933bb451f526e11133acd08 Mon Sep 17 00:00:00 2001 From: Shahar Papini Date: Fri, 22 Mar 2024 20:36:33 +0200 Subject: [PATCH] FRI using simple merkle --- src/commitment_scheme/prover.rs | 10 +++ src/core/commitment_scheme/prover.rs | 6 +- src/core/fri.rs | 97 ++++++++++++++-------------- 3 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/commitment_scheme/prover.rs b/src/commitment_scheme/prover.rs index 108afd61d..cfd91f192 100644 --- a/src/commitment_scheme/prover.rs +++ b/src/commitment_scheme/prover.rs @@ -78,6 +78,7 @@ impl, H: MerkleHasher> MerkleProver { columns: Vec<&Col>, ) -> (ColumnVec>, MerkleDecommitment) { // Check that queries are sorted and deduped. + // TODO(andrew): Consider using a Queries struct to prevent this. for queries in queries_per_log_size.values() { assert!( queries.windows(2).all(|w| w[0] < w[1]), @@ -220,3 +221,12 @@ impl MerkleDecommitment { } } } +// TODO(andreW): Remove these in favor of the `derivative` crate. +impl Clone for MerkleDecommitment { + fn clone(&self) -> Self { + Self { + hash_witness: self.hash_witness.clone(), + column_witness: self.column_witness.clone(), + } + } +} diff --git a/src/core/commitment_scheme/prover.rs b/src/core/commitment_scheme/prover.rs index e2e5385e5..455541163 100644 --- a/src/core/commitment_scheme/prover.rs +++ b/src/core/commitment_scheme/prover.rs @@ -18,7 +18,7 @@ use super::super::prover::{ use super::super::ColumnVec; use super::quotients::{compute_fri_quotients, PointSample}; use super::utils::TreeVec; -use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher}; +use crate::commitment_scheme::blake2_hash::Blake2sHash; use crate::commitment_scheme::blake2_merkle::Blake2sMerkleHasher; use crate::commitment_scheme::prover::{MerkleDecommitment, MerkleProver}; use crate::core::channel::Channel; @@ -91,7 +91,7 @@ impl CommitmentSchemeProver { // Run FRI commitment phase on the oods quotients. let fri_config = FriConfig::new(LOG_LAST_LAYER_DEGREE_BOUND, LOG_BLOWUP_FACTOR, N_QUERIES); let fri_prover = - FriProver::::commit(channel, fri_config, "ients); + FriProver::::commit(channel, fri_config, "ients); // Proof of work. let proof_of_work = ProofOfWork::new(PROOF_OF_WORK_BITS).prove(channel); @@ -127,7 +127,7 @@ pub struct CommitmentSchemeProof { pub decommitments: TreeVec>, pub queried_values: TreeVec>>, pub proof_of_work: ProofOfWorkProof, - pub fri_proof: FriProof, + pub fri_proof: FriProof, } /// Prover data for a single commitment tree in a commitment scheme. The commitment scheme allows to diff --git a/src/core/fri.rs b/src/core/fri.rs index 1dcaaa4e6..adbfb0c9e 100644 --- a/src/core/fri.rs +++ b/src/core/fri.rs @@ -10,16 +10,16 @@ use thiserror::Error; use super::backend::{Backend, CPUBackend}; use super::channel::Channel; -use super::fields::m31::BaseField; use super::fields::qm31::SecureField; +use super::fields::secure_column::SecureColumn; use super::poly::circle::{CircleEvaluation, SecureEvaluation}; use super::poly::line::{LineEvaluation, LinePoly}; use super::poly::BitReversedOrder; // TODO(andrew): Create fri/ directory, move queries.rs there and split this file up. use super::queries::{Queries, SparseSubCircleDomain}; -use crate::commitment_scheme::hasher::Hasher; -use crate::commitment_scheme::merkle_decommitment::MerkleDecommitment; -use crate::commitment_scheme::merkle_tree::MerkleTree; +use crate::commitment_scheme::ops::{MerkleHasher, MerkleOps}; +use crate::commitment_scheme::prover::{MerkleDecommitment, MerkleProver}; +use crate::commitment_scheme::verifier::MerkleVerifier; use crate::core::circle::Coset; use crate::core::poly::line::LineDomain; use crate::core::utils::bit_reverse_index; @@ -104,7 +104,7 @@ pub trait FriOps: Backend + Sized { } /// A FRI prover that applies the FRI protocol to prove a set of polynomials are of low degree. -pub struct FriProver { +pub struct FriProver, H: MerkleHasher> { config: FriConfig, inner_layers: Vec>, last_layer_poly: LinePoly, @@ -112,7 +112,7 @@ pub struct FriProver { column_log_sizes: Vec, } -impl> FriProver { +impl, H: MerkleHasher> FriProver { /// Commits to multiple [CircleEvaluation]s. /// /// `columns` must be provided in descending order by size. @@ -273,7 +273,7 @@ impl> FriProver { } } -pub struct FriVerifier { +pub struct FriVerifier { config: FriConfig, /// Alpha used to fold all circle polynomials to univariate polynomials. circle_poly_alpha: SecureField, @@ -289,7 +289,7 @@ pub struct FriVerifier { queries: Option, } -impl> FriVerifier { +impl FriVerifier { /// Verifies the commitment stage of FRI. /// /// `column_bounds` should be the committed circle polynomial degree bounds in descending order. @@ -328,7 +328,7 @@ impl> FriVerifier { )); for (layer_index, proof) in proof.inner_layers.into_iter().enumerate() { - channel.mix_digest(proof.commitment); + channel.mix_digest(proof.commitment.clone()); let folding_alpha = channel.draw_felt(); @@ -591,7 +591,7 @@ impl LinePolyDegreeBound { /// A FRI proof. #[derive(Debug)] -pub struct FriProof { +pub struct FriProof { pub inner_layers: Vec>, pub last_layer_poly: LinePoly, } @@ -607,15 +607,15 @@ pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1; /// /// The subset corresponds to the set of evaluations needed by a FRI verifier. #[derive(Debug)] -pub struct FriLayerProof { +pub struct FriLayerProof { /// The subset stored corresponds to the set of evaluations the verifier doesn't have but needs /// to fold and verify the merkle decommitment. pub evals_subset: Vec, - pub decommitment: MerkleDecommitment, + pub decommitment: MerkleDecommitment, pub commitment: H::Hash, } -struct FriLayerVerifier { +struct FriLayerVerifier { degree_bound: LinePolyDegreeBound, domain: LineDomain, folding_alpha: SecureField, @@ -623,7 +623,7 @@ struct FriLayerVerifier { proof: FriLayerProof, } -impl> FriLayerVerifier { +impl FriLayerVerifier { /// Verifies the layer's merkle decommitment and returns the the folded queries and query evals. /// /// # Errors @@ -640,38 +640,18 @@ impl> FriLayerVerifier { queries: Queries, evals_at_queries: Vec, ) -> Result<(Queries, Vec), FriVerificationError> { - let decommitment = &self.proof.decommitment; - let commitment = self.proof.commitment; + let decommitment = self.proof.decommitment.clone(); + let commitment = self.proof.commitment.clone(); // Extract the evals needed for decommitment and folding. let sparse_evaluation = self.extract_evaluation(&queries, &evals_at_queries)?; // TODO: When leaf values are removed from the decommitment, also remove this block. - { - let mut expected_decommitment_evals = Vec::new(); - - for leaf in decommitment.values() { - // Ensure each leaf is a single value. - if let Ok(evals) = leaf.try_into() { - expected_decommitment_evals.push(SecureField::from_m31_array(evals)); - } else { - return Err(FriVerificationError::InnerLayerCommitmentInvalid { - layer: self.layer_index, - }); - } - } - - let actual_decommitment_evals = sparse_evaluation - .subline_evals - .iter() - .flat_map(|e| e.values.into_iter()); - - if !actual_decommitment_evals.eq(expected_decommitment_evals) { - return Err(FriVerificationError::InnerLayerCommitmentInvalid { - layer: self.layer_index, - }); - } - } + let actual_decommitment_evals: SecureColumn = sparse_evaluation + .subline_evals + .iter() + .flat_map(|e| e.values.into_iter()) + .collect(); let folded_queries = queries.fold(FOLD_STEP); @@ -685,7 +665,21 @@ impl> FriLayerVerifier { }) .collect::>(); - if !decommitment.verify(commitment, &decommitment_positions) { + let merkle_verifier = MerkleVerifier { + root: commitment, + column_log_sizes: vec![self.domain.log_size(); 4], + }; + // TODO(spapini): Propagate error. + if merkle_verifier + .verify( + [(self.domain.log_size(), decommitment_positions)] + .into_iter() + .collect(), + actual_decommitment_evals.columns.into_iter().collect_vec(), + decommitment, + ) + .is_err() + { return Err(FriVerificationError::InnerLayerCommitmentInvalid { layer: self.layer_index, }); @@ -768,16 +762,16 @@ impl> FriLayerVerifier { /// The polynomial evaluations are viewed as evaluation of a polynomial on multiple distinct cosets /// of size two. Each leaf of the merkle tree commits to a single coset evaluation. // TODO(andrew): Support different step sizes. -struct FriLayerProver { +struct FriLayerProver, H: MerkleHasher> { evaluation: LineEvaluation, - merkle_tree: MerkleTree, + merkle_tree: MerkleProver, } -impl> FriLayerProver { +impl, H: MerkleHasher> FriLayerProver { fn new(evaluation: LineEvaluation) -> Self { // TODO(spapini): Commit on slice. // TODO(spapini): Merkle tree in backend. - let merkle_tree = MerkleTree::commit(evaluation.values.to_cpu().columns.into()); + let merkle_tree = MerkleProver::commit(evaluation.values.columns.iter().collect_vec()); #[allow(unreachable_code)] FriLayerProver { evaluation, @@ -813,7 +807,12 @@ impl> FriLayerProver { } let commitment = self.merkle_tree.root(); - let decommitment = self.merkle_tree.generate_decommitment(decommit_positions); + let (_, decommitment) = self.merkle_tree.decommit( + [(self.evaluation.domain().log_size(), decommit_positions)] + .into_iter() + .collect(), + self.evaluation.values.columns.iter().collect_vec(), + ); FriLayerProof { evals_subset, @@ -904,7 +903,7 @@ mod tests { use num_traits::{One, Zero}; use super::{get_opening_positions, FriVerificationError, SparseCircleEvaluation}; - use crate::commitment_scheme::blake2_hash::Blake2sHasher; + use crate::commitment_scheme::blake2_merkle::Blake2sMerkleHasher; use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly}; use crate::core::backend::{CPUBackend, Col, Column, ColumnOps}; use crate::core::circle::{CirclePointIndex, Coset}; @@ -925,7 +924,7 @@ mod tests { /// Default blowup factor used for tests. const LOG_BLOWUP_FACTOR: u32 = 2; - type FriProver = super::FriProver; + type FriProver = super::FriProver; #[test] fn fold_line_works() {