diff --git a/src/commitment_scheme/blake2_merkle.rs b/src/commitment_scheme/blake2_merkle.rs index 5fad8755e..4185bf08e 100644 --- a/src/commitment_scheme/blake2_merkle.rs +++ b/src/commitment_scheme/blake2_merkle.rs @@ -1,15 +1,15 @@ use itertools::Itertools; use num_traits::Zero; +use super::blake2_hash::Blake2sHash; use super::blake2s_ref::compress; use super::ops::{MerkleHasher, MerkleOps}; use crate::core::backend::CPUBackend; use crate::core::fields::m31::BaseField; -#[derive(Copy, Clone, PartialEq, Eq, Default)] -pub struct Blake2sHash(pub [u32; 8]); -pub struct Blake2Hasher; -impl MerkleHasher for Blake2Hasher { +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub struct Blake2sMerkleHasher; +impl MerkleHasher for Blake2sMerkleHasher { type Hash = Blake2sHash; fn hash_node( @@ -35,21 +35,11 @@ impl MerkleHasher for Blake2Hasher { for chunk in padded_values.array_chunks::<16>() { state = compress(state, unsafe { std::mem::transmute(chunk) }, 0, 0, 0, 0); } - Blake2sHash(state) + state.map(|x| x.to_le_bytes()).flatten().into() } } -impl std::fmt::Debug for Blake2sHash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // Write as hex. - for &byte in self.0.iter() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} - -impl MerkleOps for CPUBackend { +impl MerkleOps for CPUBackend { fn commit_on_layer( log_size: u32, prev_layer: Option<&Vec>, @@ -57,7 +47,7 @@ impl MerkleOps for CPUBackend { ) -> Vec { (0..(1 << log_size)) .map(|i| { - Blake2Hasher::hash_node( + Blake2sMerkleHasher::hash_node( prev_layer.map(|prev_layer| (prev_layer[2 * i], prev_layer[2 * i + 1])), &columns.iter().map(|column| column[i]).collect_vec(), ) @@ -75,7 +65,7 @@ mod tests { use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - use crate::commitment_scheme::blake2_merkle::{Blake2Hasher, Blake2sHash}; + use crate::commitment_scheme::blake2_merkle::{Blake2sHash, Blake2sMerkleHasher}; use crate::commitment_scheme::prover::{MerkleDecommitment, MerkleProver}; use crate::commitment_scheme::verifier::{MerkleVerificationError, MerkleVerifier}; use crate::core::backend::CPUBackend; @@ -83,9 +73,9 @@ mod tests { type TestData = ( BTreeMap>, - MerkleDecommitment, + MerkleDecommitment, Vec>, - MerkleVerifier, + MerkleVerifier, ); fn prepare_merkle() -> TestData { const N_COLS: usize = 400; @@ -104,7 +94,8 @@ mod tests { .collect_vec() }) .collect_vec(); - let merkle = MerkleProver::::commit(cols.iter().collect_vec()); + let merkle = + MerkleProver::::commit(cols.iter().collect_vec()); let mut queries = BTreeMap::>::new(); for log_size in log_size_range.rev() { @@ -135,7 +126,7 @@ mod tests { #[test] fn test_merkle_invalid_witness() { let (queries, mut decommitment, values, verifier) = prepare_merkle(); - decommitment.hash_witness[20] = Blake2sHash([0; 8]); + decommitment.hash_witness[20] = Blake2sHash::default(); assert_eq!( verifier.verify(queries, values, decommitment).unwrap_err(), @@ -190,7 +181,7 @@ mod tests { #[test] fn test_merkle_witness_too_long() { let (queries, mut decommitment, values, verifier) = prepare_merkle(); - decommitment.hash_witness.push(Blake2sHash([0; 8])); + decommitment.hash_witness.push(Blake2sHash::default()); assert_eq!( verifier.verify(queries, values, decommitment).unwrap_err(), diff --git a/src/commitment_scheme/ops.rs b/src/commitment_scheme/ops.rs index 65660f887..2ea09d1da 100644 --- a/src/commitment_scheme/ops.rs +++ b/src/commitment_scheme/ops.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + use crate::core::backend::{Col, ColumnOps}; use crate::core::fields::m31::BaseField; @@ -8,7 +10,7 @@ use crate::core::fields::m31::BaseField; /// children hashes. /// At each layer, the tree may have multiple columns of the same length as the layer. /// Each node in that layer contains one value from each column. -pub trait MerkleHasher { +pub trait MerkleHasher: Debug { type Hash: Clone + Eq + std::fmt::Debug; /// Hashes a single Merkle node. See [MerkleHasher] for more details. fn hash_node( diff --git a/src/commitment_scheme/verifier.rs b/src/commitment_scheme/verifier.rs index e6b54592c..076253d33 100644 --- a/src/commitment_scheme/verifier.rs +++ b/src/commitment_scheme/verifier.rs @@ -16,6 +16,12 @@ pub struct MerkleVerifier { pub column_log_sizes: Vec, } impl MerkleVerifier { + pub fn new(root: H::Hash, column_log_sizes: Vec) -> Self { + Self { + root, + column_log_sizes, + } + } /// Verifies the decommitment of the columns. /// /// # Arguments diff --git a/src/core/commitment_scheme/mod.rs b/src/core/commitment_scheme/mod.rs index 093dcef0f..3b7a6e96a 100644 --- a/src/core/commitment_scheme/mod.rs +++ b/src/core/commitment_scheme/mod.rs @@ -13,4 +13,4 @@ mod verifier; pub use self::prover::{CommitmentSchemeProof, CommitmentSchemeProver}; pub use self::utils::TreeVec; -pub use self::verifier::{CommitmentSchemeVerifier, CommitmentTreeVerifier}; +pub use self::verifier::CommitmentSchemeVerifier; diff --git a/src/core/commitment_scheme/prover.rs b/src/core/commitment_scheme/prover.rs index f2172b06d..adfc29c4d 100644 --- a/src/core/commitment_scheme/prover.rs +++ b/src/core/commitment_scheme/prover.rs @@ -1,5 +1,4 @@ -use std::iter::zip; -use std::ops::Deref; +use std::collections::BTreeMap; use itertools::Itertools; @@ -20,13 +19,12 @@ 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::merkle_input::{MerkleTreeColumnLayout, MerkleTreeInput}; -use crate::commitment_scheme::mixed_degree_decommitment::MixedDecommitment; -use crate::commitment_scheme::mixed_degree_merkle_tree::MixedDegreeMerkleTree; +use crate::commitment_scheme::blake2_merkle::Blake2sMerkleHasher; +use crate::commitment_scheme::prover::{MerkleDecommitment, MerkleProver}; use crate::core::channel::Channel; use crate::core::poly::circle::SecureEvaluation; -type MerkleHasher = Blake2sHasher; +type MerkleHasher = Blake2sMerkleHasher; type ProofChannel = Blake2sChannel; /// The prover side of a FRI polynomial commitment scheme. See [super]. @@ -49,7 +47,7 @@ impl CommitmentSchemeProver { } pub fn roots(&self) -> TreeVec { - self.trees.as_ref().map(|tree| tree.root()) + self.trees.as_ref().map(|tree| tree.commitment.root()) } pub fn polynomials(&self) -> TreeVec> { @@ -101,7 +99,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); @@ -111,12 +109,9 @@ impl CommitmentSchemeProver { // Decommit the FRI queries on the merkle trees. let decommitment_results = self.trees.as_ref().map(|tree| { - let queries = tree - .polynomials + let queries = fri_query_domains .iter() - .map(|poly| { - fri_query_domains[&(poly.log_size() + self.log_blowup_factor)].flatten() - }) + .map(|(&log_size, domain)| (log_size, domain.flatten())) .collect(); tree.decommit(queries) }); @@ -137,10 +132,10 @@ impl CommitmentSchemeProver { #[derive(Debug)] pub struct CommitmentSchemeProof { pub sampled_values: TreeVec>>, - pub decommitments: TreeVec>, + 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 @@ -148,8 +143,7 @@ pub struct CommitmentSchemeProof { pub struct CommitmentTreeProver { pub polynomials: ColumnVec, pub evaluations: ColumnVec>, - pub commitment: MixedDegreeMerkleTree, - column_layout: MerkleTreeColumnLayout, + pub commitment: MerkleProver, } impl CommitmentTreeProver { @@ -167,64 +161,29 @@ impl CommitmentTreeProver { }) .collect_vec(); - let mut merkle_input = MerkleTreeInput::new(); - const LOG_N_BASE_FIELD_ELEMENTS_IN_SACK: u32 = 4; - - for eval in evaluations.iter() { - // The desired depth for a column of log_length n is such that Blake2s hashes are - // filled(64B). Explicitly: There are 2^(d-1) hash 'sacks' at depth d, - // hence, with elements of 4 bytes, 2^(d-1) = 2^n / 16, => d = n-3. - let inject_depth = std::cmp::max::( - eval.domain.log_size() as i32 - (LOG_N_BASE_FIELD_ELEMENTS_IN_SACK as i32 - 1), - 1, - ); - merkle_input.insert_column(inject_depth as usize, &eval.values); - } - - let (tree, root) = - MixedDegreeMerkleTree::::commit_default(&merkle_input); - channel.mix_digest(root); - - let column_layout = merkle_input.column_layout(); + let tree = MerkleProver::commit(evaluations.iter().map(|eval| &eval.values).collect()); + channel.mix_digest(tree.root()); CommitmentTreeProver { polynomials, evaluations, commitment: tree, - column_layout, } } /// Decommits the merkle tree on the given query positions. + /// Returns the values at the queried positions and the decommitment. + /// The queries are given as a mapping from the log size of the layer size to the queried + /// positions on each column of that size. fn decommit( &self, - queries: ColumnVec>, - ) -> ( - ColumnVec>, - MixedDecommitment, - ) { - let values = zip(&self.evaluations, &queries) - .map(|(column, column_queries)| column_queries.iter().map(|q| column[*q]).collect()) - .collect(); - - // Rebuild the merkle input for now. - // TODO(Ohad): change after tree refactor. Consider removing the input struct and have the - // decommitment take queries and columns only. + queries: BTreeMap>, + ) -> (ColumnVec>, MerkleDecommitment) { let eval_vec = self .evaluations .iter() - .map(|eval| &eval.values[..]) + .map(|eval| &eval.values) .collect_vec(); - let input = self.column_layout.build_input(&eval_vec); - let decommitment = self.commitment.decommit(&input, &queries); - (values, decommitment) - } -} - -impl Deref for CommitmentTreeProver { - type Target = MixedDegreeMerkleTree; - - fn deref(&self) -> &Self::Target { - &self.commitment + self.commitment.decommit(queries, eval_vec) } } diff --git a/src/core/commitment_scheme/verifier.rs b/src/core/commitment_scheme/verifier.rs index 253d086a9..ffdc29fd7 100644 --- a/src/core/commitment_scheme/verifier.rs +++ b/src/core/commitment_scheme/verifier.rs @@ -4,7 +4,6 @@ use itertools::Itertools; use super::super::channel::Blake2sChannel; use super::super::circle::CirclePoint; -use super::super::fields::m31::BaseField; use super::super::fields::qm31::SecureField; use super::super::fri::{CirclePolyDegreeBound, FriConfig, FriVerifier}; use super::super::proof_of_work::ProofOfWork; @@ -14,8 +13,9 @@ use super::super::prover::{ use super::quotients::{fri_answers, PointSample}; use super::utils::TreeVec; use super::CommitmentSchemeProof; -use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher}; -use crate::commitment_scheme::mixed_degree_decommitment::MixedDecommitment; +use crate::commitment_scheme::blake2_hash::Blake2sHash; +use crate::commitment_scheme::blake2_merkle::Blake2sMerkleHasher; +use crate::commitment_scheme::verifier::MerkleVerifier; use crate::core::channel::Channel; use crate::core::prover::VerificationError; use crate::core::ColumnVec; @@ -25,7 +25,7 @@ type ProofChannel = Blake2sChannel; /// The verifier side of a FRI polynomial commitment scheme. See [super]. #[derive(Default)] pub struct CommitmentSchemeVerifier { - pub trees: TreeVec, + pub trees: TreeVec>, } impl CommitmentSchemeVerifier { @@ -35,7 +35,9 @@ impl CommitmentSchemeVerifier { /// A [TreeVec] of the log sizes of each column in each commitment tree. fn column_log_sizes(&self) -> TreeVec> { - self.trees.as_ref().map(|tree| tree.log_sizes.to_vec()) + self.trees + .as_ref() + .map(|tree| tree.column_log_sizes.clone()) } /// Reads a commitment from the prover. @@ -45,7 +47,12 @@ impl CommitmentSchemeVerifier { log_sizes: Vec, channel: &mut ProofChannel, ) { - let verifier = CommitmentTreeVerifier::new(commitment, log_sizes, channel); + channel.mix_digest(commitment); + let extended_log_sizes = log_sizes + .iter() + .map(|&log_size| log_size + LOG_BLOWUP_FACTOR) + .collect(); + let verifier = MerkleVerifier::new(commitment, extended_log_sizes); self.trees.push(verifier); } @@ -62,7 +69,7 @@ impl CommitmentSchemeVerifier { .column_log_sizes() .zip_cols(&sampled_points) .map_cols(|(log_size, sampled_points)| { - vec![CirclePolyDegreeBound::new(log_size); sampled_points.len()] + vec![CirclePolyDegreeBound::new(log_size - LOG_BLOWUP_FACTOR); sampled_points.len()] }) .flatten_cols() .into_iter() @@ -82,24 +89,20 @@ impl CommitmentSchemeVerifier { let fri_query_domains = fri_verifier.column_query_positions(channel); // Verify merkle decommitments. - let merkle_verification_result = self - .trees + self.trees .as_ref() - .zip(&proof.decommitments) - .map(|(tree, decommitment)| { - // TODO(spapini): Also verify queried_values here. - let queries = tree - .log_sizes + .zip(proof.decommitments) + .zip(proof.queried_values.clone()) + .map(|((tree, decommitment), queried_values)| { + let queries = fri_query_domains .iter() - .map(|log_size| fri_query_domains[&(log_size + LOG_BLOWUP_FACTOR)].flatten()) - .collect_vec(); - tree.verify(decommitment, &queries) + .map(|(&log_size, domain)| (log_size, domain.flatten())) + .collect(); + tree.verify(queries, queried_values, decommitment) }) - .iter() - .all(|x| *x); - if !merkle_verification_result { - return Err(VerificationError::MerkleVerificationFailed); - } + .0 + .into_iter() + .collect::>()?; // Answer FRI queries. let samples = sampled_points @@ -117,7 +120,7 @@ impl CommitmentSchemeVerifier { self.column_log_sizes() .flatten() .into_iter() - .map(|x| x + LOG_BLOWUP_FACTOR) + // .map(|x| x) .collect(), &samples, random_coeff, @@ -129,31 +132,3 @@ impl CommitmentSchemeVerifier { Ok(()) } } - -/// Verifier data for a single commitment tree in a commitment scheme. -pub struct CommitmentTreeVerifier { - pub commitment: Blake2sHash, - pub log_sizes: Vec, -} - -impl CommitmentTreeVerifier { - pub fn new(commitment: Blake2sHash, log_sizes: Vec, channel: &mut ProofChannel) -> Self { - channel.mix_digest(commitment); - CommitmentTreeVerifier { - commitment, - log_sizes, - } - } - - pub fn verify( - &self, - decommitment: &MixedDecommitment, - queries: &[Vec], - ) -> bool { - decommitment.verify( - self.commitment, - queries, - decommitment.queried_values.iter().copied(), - ) - } -} diff --git a/src/core/prover/mod.rs b/src/core/prover/mod.rs index 8e4b20206..41cc419e2 100644 --- a/src/core/prover/mod.rs +++ b/src/core/prover/mod.rs @@ -8,6 +8,7 @@ use super::proof_of_work::ProofOfWorkVerificationError; use super::ColumnVec; use crate::commitment_scheme::blake2_hash::Blake2sHasher; use crate::commitment_scheme::hasher::Hasher; +use crate::commitment_scheme::verifier::MerkleVerificationError; use crate::core::air::{Air, AirExt}; use crate::core::backend::cpu::CPUCircleEvaluation; use crate::core::backend::CPUBackend; @@ -207,8 +208,8 @@ pub enum ProvingError { pub enum VerificationError { #[error("Proof has invalid structure: {0}.")] InvalidStructure(String), - #[error("Merkle verification failed.")] - MerkleVerificationFailed, + #[error(transparent)] + Merkle(#[from] MerkleVerificationError), #[error( "The composition polynomial OODS value does not match the trace OODS values (DEEP-ALI failure)." diff --git a/src/examples/fibonacci/mod.rs b/src/examples/fibonacci/mod.rs index 880b82459..d179cc2d8 100644 --- a/src/examples/fibonacci/mod.rs +++ b/src/examples/fibonacci/mod.rs @@ -109,6 +109,8 @@ impl MultiFibonacci { #[cfg(test)] mod tests { + use std::assert_matches::assert_matches; + use itertools::Itertools; use num_traits::One; @@ -211,7 +213,7 @@ mod tests { invalid_proof.commitment_scheme_proof.queried_values.0[0][0][4] += BaseField::one(); let error = fib.verify(invalid_proof).unwrap_err(); - assert!(matches!(error, VerificationError::Fri(_))); + assert_matches!(error, VerificationError::Merkle(_)); } #[test] @@ -226,7 +228,7 @@ mod tests { .swap(0, 1); let error = fib.verify(invalid_proof).unwrap_err(); - assert!(matches!(error, VerificationError::InvalidStructure(_))); + assert_matches!(error, VerificationError::InvalidStructure(_)); assert!(error .to_string() .contains("Unexpected sampled_values structure")); @@ -241,10 +243,7 @@ mod tests { invalid_proof.commitment_scheme_proof.queried_values.0[0][0].pop(); let error = fib.verify(invalid_proof).unwrap_err(); - assert!(matches!(error, VerificationError::InvalidStructure(_))); - assert!(error - .to_string() - .contains("Insufficient number of queried values")); + assert_matches!(error, VerificationError::Merkle(_)); } #[test] diff --git a/src/lib.rs b/src/lib.rs index ac21515a6..e5e707d64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,9 @@ slice_group_by, stdsimd, get_many_mut, - int_roundings + int_roundings, + slice_flatten, + assert_matches )] pub mod commitment_scheme; pub mod core;