From 65b9bc059882bfba0bc8a10b871101f1642b2f85 Mon Sep 17 00:00:00 2001
From: Shahar Papini <43779613+spapinistarkware@users.noreply.github.com>
Date: Wed, 3 Apr 2024 14:55:59 +0300
Subject: [PATCH] FRI using simple merkle (#530)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This change is [](https://reviewable.io/reviews/starkware-libs/stwo/530)
---
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() {