From f0abe55f6fe1142ea79bcae140b6cf3f8d3e8416 Mon Sep 17 00:00:00 2001
From: Alon Haramati <91828241+alonh5@users.noreply.github.com>
Date: Wed, 20 Mar 2024 10:13:38 +0200
Subject: [PATCH] Add errors in prove. (#468)
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/468)
---
src/commitment_scheme/blake2_hash.rs | 2 +-
src/commitment_scheme/merkle_decommitment.rs | 2 +-
src/core/commitment_scheme/mod.rs | 2 +
src/core/constraints.rs | 2 +-
src/core/fri.rs | 11 +-
src/core/mod.rs | 3 +
src/core/poly/circle/domain.rs | 6 +-
src/core/poly/circle/mod.rs | 2 +-
src/core/prover/mod.rs | 150 ++++++++++++++++++-
src/core/test_utils.rs | 21 +++
src/core/utils.rs | 10 --
src/fibonacci/mod.rs | 19 +--
12 files changed, 195 insertions(+), 35 deletions(-)
create mode 100644 src/core/test_utils.rs
diff --git a/src/commitment_scheme/blake2_hash.rs b/src/commitment_scheme/blake2_hash.rs
index 9b0f8691e..cc53828e6 100644
--- a/src/commitment_scheme/blake2_hash.rs
+++ b/src/commitment_scheme/blake2_hash.rs
@@ -64,7 +64,7 @@ impl super::hasher::Name for Blake2sHash {
impl super::hasher::Hash for Blake2sHash {}
// Wrapper for the blake2s Hashing functionalities.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct Blake2sHasher {
state: Blake2s256,
}
diff --git a/src/commitment_scheme/merkle_decommitment.rs b/src/commitment_scheme/merkle_decommitment.rs
index 4273a230f..4c0c129e0 100644
--- a/src/commitment_scheme/merkle_decommitment.rs
+++ b/src/commitment_scheme/merkle_decommitment.rs
@@ -14,7 +14,7 @@ use crate::core::fields::IntoSlice;
/// queried path, or nodes with both children in the queried path are excluded.
/// * `n_rows_in_leaf_block` - The number of trace-rows packed in each leaf block.
// TODO(Ohad): derive Debug.
-#[derive(Default)]
+#[derive(Default, Debug)]
pub struct MerkleDecommitment {
pub leaf_blocks: Vec>,
pub layers: Vec>,
diff --git a/src/core/commitment_scheme/mod.rs b/src/core/commitment_scheme/mod.rs
index 1459e27bd..47d4f1f60 100644
--- a/src/core/commitment_scheme/mod.rs
+++ b/src/core/commitment_scheme/mod.rs
@@ -20,6 +20,7 @@ pub mod utils;
/// Holds a vector for each tree, which holds a vector for each column, which holds its respective
/// opened values.
+#[derive(Debug)]
pub struct OpenedValues(pub Vec>>);
impl Deref for OpenedValues {
@@ -30,6 +31,7 @@ impl Deref for OpenedValues {
}
}
+#[derive(Debug)]
pub struct Decommitments(Vec>);
impl Deref for Decommitments {
diff --git a/src/core/constraints.rs b/src/core/constraints.rs
index bc84a1638..e9c141992 100644
--- a/src/core/constraints.rs
+++ b/src/core/constraints.rs
@@ -103,7 +103,7 @@ mod tests {
use crate::core::fields::{ComplexConjugate, FieldExpOps};
use crate::core::poly::circle::CanonicCoset;
use crate::core::poly::NaturalOrder;
- use crate::core::utils::secure_eval_to_base_eval;
+ use crate::core::test_utils::secure_eval_to_base_eval;
use crate::m31;
#[test]
diff --git a/src/core/fri.rs b/src/core/fri.rs
index b7f8971f4..ee3e598d6 100644
--- a/src/core/fri.rs
+++ b/src/core/fri.rs
@@ -619,6 +619,7 @@ impl LinePolyDegreeBound {
}
/// A FRI proof.
+#[derive(Debug)]
pub struct FriProof {
pub inner_layers: Vec>,
pub last_layer_poly: LinePoly,
@@ -634,6 +635,7 @@ pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1;
/// Stores a subset of evaluations in a [FriLayer] with their corresponding merkle decommitments.
///
/// The subset corresponds to the set of evaluations needed by a FRI verifier.
+#[derive(Debug)]
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.
@@ -911,10 +913,9 @@ mod tests {
use num_traits::{One, Zero};
use super::{get_opening_positions, SparseCircleEvaluation, VerificationError};
- use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher};
+ use crate::commitment_scheme::blake2_hash::Blake2sHasher;
use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly, CPULineEvaluation};
use crate::core::backend::CPUBackend;
- use crate::core::channel::{Blake2sChannel, Channel};
use crate::core::circle::{CirclePointIndex, Coset};
use crate::core::fields::m31::BaseField;
use crate::core::fields::qm31::SecureField;
@@ -926,6 +927,7 @@ mod tests {
use crate::core::poly::line::{LineDomain, LineEvaluation, LinePoly};
use crate::core::poly::{BitReversedOrder, NaturalOrder};
use crate::core::queries::{Queries, SparseSubCircleDomain};
+ use crate::core::test_utils::test_channel;
/// Default blowup factor used for tests.
const LOG_BLOWUP_FACTOR: u32 = 2;
@@ -1278,9 +1280,4 @@ mod tests {
SparseCircleEvaluation::new(coset_evals)
}
-
- fn test_channel() -> Blake2sChannel {
- let seed = Blake2sHash::from(vec![0; 32]);
- Blake2sChannel::new(seed)
- }
}
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 6bf765838..876fccfb4 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -14,12 +14,15 @@ pub mod poly;
pub mod proof_of_work;
pub mod prover;
pub mod queries;
+#[cfg(test)]
+pub mod test_utils;
pub mod utils;
/// A vector in which each element relates (by index) to a column in the trace.
pub type ColumnVec = Vec;
/// A vector of [ColumnVec]s. Each [ColumnVec] relates (by index) to a component in the air.
+#[derive(Debug)]
pub struct ComponentVec(pub Vec>);
impl ComponentVec> {
diff --git a/src/core/poly/circle/domain.rs b/src/core/poly/circle/domain.rs
index 3a69d4611..4cf598697 100644
--- a/src/core/poly/circle/domain.rs
+++ b/src/core/poly/circle/domain.rs
@@ -1,8 +1,12 @@
use std::iter::Chain;
-use crate::core::circle::{CirclePoint, CirclePointIndex, Coset, CosetIterator};
+use crate::core::circle::{
+ CirclePoint, CirclePointIndex, Coset, CosetIterator, M31_CIRCLE_LOG_ORDER,
+};
use crate::core::fields::m31::BaseField;
+pub const MAX_CIRCLE_DOMAIN_LOG_SIZE: u32 = M31_CIRCLE_LOG_ORDER - 1;
+
/// A valid domain for circle polynomial interpolation and evaluation.
/// Valid domains are a disjoint union of two conjugate cosets: +-C + .
/// The ordering defined on this domain is C + iG_n, and then -C - iG_n.
diff --git a/src/core/poly/circle/mod.rs b/src/core/poly/circle/mod.rs
index a6810f259..ab90285a0 100644
--- a/src/core/poly/circle/mod.rs
+++ b/src/core/poly/circle/mod.rs
@@ -6,7 +6,7 @@ mod poly;
mod secure_poly;
pub use canonic::CanonicCoset;
-pub use domain::CircleDomain;
+pub use domain::{CircleDomain, MAX_CIRCLE_DOMAIN_LOG_SIZE};
pub use evaluation::{CircleEvaluation, CosetSubEvaluation};
pub use ops::PolyOps;
pub use poly::CirclePoly;
diff --git a/src/core/prover/mod.rs b/src/core/prover/mod.rs
index 9c0332a54..d19392848 100644
--- a/src/core/prover/mod.rs
+++ b/src/core/prover/mod.rs
@@ -1,8 +1,9 @@
use std::iter::zip;
use itertools::{enumerate, Itertools};
+use thiserror::Error;
-use super::poly::circle::CanonicCoset;
+use super::poly::circle::{CanonicCoset, MAX_CIRCLE_DOMAIN_LOG_SIZE};
use super::queries::SparseSubCircleDomain;
use super::ColumnVec;
use crate::commitment_scheme::blake2_hash::Blake2sHasher;
@@ -33,6 +34,7 @@ pub const LOG_LAST_LAYER_DEGREE_BOUND: u32 = 0;
pub const PROOF_OF_WORK_BITS: u32 = 12;
pub const N_QUERIES: usize = 3;
+#[derive(Debug)]
pub struct StarkProof {
pub commitments: Vec<::Hash>,
pub decommitments: Decommitments,
@@ -44,6 +46,7 @@ pub struct StarkProof {
pub additional_proof_data: AdditionalProofData,
}
+#[derive(Debug)]
pub struct AdditionalProofData {
pub composition_polynomial_oods_value: SecureField,
pub composition_polynomial_random_coeff: SecureField,
@@ -55,7 +58,25 @@ pub fn prove(
air: &impl Air,
channel: &mut Channel,
trace: ColumnVec>,
-) -> StarkProof {
+) -> Result {
+ // Check that traces are not too big.
+ for (i, trace) in trace.iter().enumerate() {
+ if trace.domain.log_size() + LOG_BLOWUP_FACTOR > MAX_CIRCLE_DOMAIN_LOG_SIZE {
+ return Err(ProvingError::MaxTraceDegreeExceeded {
+ trace_index: i,
+ degree: trace.domain.log_size(),
+ });
+ }
+ }
+
+ // Check that the composition polynomial is not too big.
+ let composition_polynomial_log_degree_bound = air.max_constraint_log_degree_bound();
+ if composition_polynomial_log_degree_bound + LOG_BLOWUP_FACTOR > MAX_CIRCLE_DOMAIN_LOG_SIZE {
+ return Err(ProvingError::MaxCompositionDegreeExceeded {
+ degree: composition_polynomial_log_degree_bound,
+ });
+ }
+
// Evaluate and commit on trace.
let trace_polys = trace.into_iter().map(|poly| poly.interpolate()).collect();
let mut commitment_scheme = CommitmentSchemeProver::new(LOG_BLOWUP_FACTOR);
@@ -115,7 +136,7 @@ pub fn prove(
let (opened_values, decommitments) = commitment_scheme.decommit(fri_opening_positions);
- StarkProof {
+ Ok(StarkProof {
commitments: commitment_scheme.roots(),
decommitments,
trace_oods_values,
@@ -129,7 +150,7 @@ pub fn prove(
oods_point,
oods_quotients,
},
- }
+ })
}
pub fn verify(proof: StarkProof, air: &impl Air, channel: &mut Channel) -> bool {
@@ -256,3 +277,124 @@ fn prepare_fri_evaluations(
}
sparse_circle_evaluations
}
+
+#[derive(Clone, Copy, Debug, Error)]
+pub enum ProvingError {
+ #[error(
+ "Trace column {trace_index} log degree bound ({degree}) exceeded max log degree ({}).",
+ MAX_CIRCLE_DOMAIN_LOG_SIZE - LOG_BLOWUP_FACTOR
+ )]
+ MaxTraceDegreeExceeded { trace_index: usize, degree: u32 },
+ #[error(
+ "Composition polynomial log degree bound ({degree}) exceeded max log degree ({}).",
+ MAX_CIRCLE_DOMAIN_LOG_SIZE - LOG_BLOWUP_FACTOR
+ )]
+ MaxCompositionDegreeExceeded { degree: u32 },
+}
+
+#[cfg(test)]
+mod tests {
+ use num_traits::Zero;
+
+ use crate::core::air::evaluation::{DomainEvaluationAccumulator, PointEvaluationAccumulator};
+ use crate::core::air::{Air, Component, ComponentTrace, ComponentVisitor, Mask};
+ use crate::core::backend::cpu::CPUCircleEvaluation;
+ use crate::core::backend::CPUBackend;
+ use crate::core::circle::{CirclePoint, CirclePointIndex, Coset};
+ use crate::core::fields::m31::BaseField;
+ use crate::core::fields::qm31::SecureField;
+ use crate::core::poly::circle::{CircleDomain, MAX_CIRCLE_DOMAIN_LOG_SIZE};
+ use crate::core::prover::{prove, ProvingError};
+ use crate::core::test_utils::test_channel;
+
+ struct TestAir>(C);
+
+ impl Air for TestAir {
+ fn visit_components>(&self, v: &mut V) {
+ v.visit(&self.0)
+ }
+ }
+
+ struct TestComponent {
+ max_constraint_log_degree_bound: u32,
+ }
+
+ impl Component for TestComponent {
+ fn max_constraint_log_degree_bound(&self) -> u32 {
+ self.max_constraint_log_degree_bound
+ }
+
+ fn trace_log_degree_bounds(&self) -> Vec {
+ todo!()
+ }
+
+ fn evaluate_constraint_quotients_on_domain(
+ &self,
+ _trace: &ComponentTrace<'_, CPUBackend>,
+ _evaluation_accumulator: &mut DomainEvaluationAccumulator,
+ ) {
+ todo!()
+ }
+
+ fn mask(&self) -> Mask {
+ todo!()
+ }
+
+ fn evaluate_constraint_quotients_at_point(
+ &self,
+ _point: CirclePoint,
+ _mask: &crate::core::ColumnVec>,
+ _evaluation_accumulator: &mut PointEvaluationAccumulator,
+ ) {
+ todo!()
+ }
+ }
+
+ // Ignored because it takes too long and too much memory (in the CI) to run.
+ #[test]
+ #[ignore]
+ fn test_trace_too_big() {
+ const LOG_DOMAIN_SIZE: u32 = MAX_CIRCLE_DOMAIN_LOG_SIZE;
+ let air = TestAir(TestComponent {
+ max_constraint_log_degree_bound: LOG_DOMAIN_SIZE,
+ });
+ let domain = CircleDomain::new(Coset::new(
+ CirclePointIndex::generator(),
+ LOG_DOMAIN_SIZE - 1,
+ ));
+ let values = vec![BaseField::zero(); 1 << LOG_DOMAIN_SIZE];
+ let trace = vec![CPUCircleEvaluation::new(domain, values)];
+
+ let proof_error = prove(&air, &mut test_channel(), trace).unwrap_err();
+ assert!(matches!(
+ proof_error,
+ ProvingError::MaxTraceDegreeExceeded {
+ trace_index: 0,
+ degree: LOG_DOMAIN_SIZE
+ }
+ ));
+ }
+
+ #[test]
+ fn test_composition_polynomial_too_big() {
+ const COMPOSITION_POLYNOMIAL_DEGREE: u32 = MAX_CIRCLE_DOMAIN_LOG_SIZE;
+ let air = TestAir(TestComponent {
+ max_constraint_log_degree_bound: COMPOSITION_POLYNOMIAL_DEGREE,
+ });
+ const LOG_DOMAIN_SIZE: u32 = 5;
+ let domain = CircleDomain::new(Coset::new(
+ CirclePointIndex::generator(),
+ LOG_DOMAIN_SIZE - 1,
+ ));
+ let values = vec![BaseField::zero(); 1 << LOG_DOMAIN_SIZE];
+ let trace = vec![CPUCircleEvaluation::new(domain, values)];
+
+ let proof_error = prove(&air, &mut test_channel(), trace).unwrap_err();
+ assert!(matches!(
+ proof_error,
+ ProvingError::MaxCompositionDegreeExceeded {
+ degree: COMPOSITION_POLYNOMIAL_DEGREE
+ }
+ ));
+ }
+}
diff --git a/src/core/test_utils.rs b/src/core/test_utils.rs
new file mode 100644
index 000000000..9633d2931
--- /dev/null
+++ b/src/core/test_utils.rs
@@ -0,0 +1,21 @@
+use super::backend::cpu::CPUCircleEvaluation;
+use super::channel::Blake2sChannel;
+use super::fields::m31::BaseField;
+use super::fields::qm31::SecureField;
+use crate::core::channel::Channel;
+
+pub fn secure_eval_to_base_eval(
+ eval: &CPUCircleEvaluation,
+) -> CPUCircleEvaluation {
+ CPUCircleEvaluation::new(
+ eval.domain,
+ eval.values.iter().map(|x| x.to_m31_array()[0]).collect(),
+ )
+}
+
+pub fn test_channel() -> Blake2sChannel {
+ use crate::commitment_scheme::blake2_hash::Blake2sHash;
+
+ let seed = Blake2sHash::from(vec![0; 32]);
+ Blake2sChannel::new(seed)
+}
diff --git a/src/core/utils.rs b/src/core/utils.rs
index c698fd05f..8a6483e36 100644
--- a/src/core/utils.rs
+++ b/src/core/utils.rs
@@ -35,16 +35,6 @@ pub fn bit_reverse(v: &mut [T]) {
}
}
-#[cfg(test)]
-pub fn secure_eval_to_base_eval(
- eval: &super::backend::cpu::CPUCircleEvaluation,
-) -> super::backend::cpu::CPUCircleEvaluation {
- super::backend::cpu::CPUCircleEvaluation::new(
- eval.domain,
- eval.values.iter().map(|x| x.to_m31_array()[0]).collect(),
- )
-}
-
#[cfg(test)]
mod tests {
use crate::core::utils::bit_reverse;
diff --git a/src/fibonacci/mod.rs b/src/fibonacci/mod.rs
index cbad21169..7683a3c23 100644
--- a/src/fibonacci/mod.rs
+++ b/src/fibonacci/mod.rs
@@ -10,7 +10,7 @@ use crate::core::fields::m31::BaseField;
use crate::core::fields::{FieldExpOps, IntoSlice};
use crate::core::poly::circle::{CanonicCoset, CircleEvaluation};
use crate::core::poly::BitReversedOrder;
-use crate::core::prover::{prove, verify, StarkProof};
+use crate::core::prover::{prove, verify, ProvingError, StarkProof};
pub mod air;
mod component;
@@ -49,7 +49,7 @@ impl Fibonacci {
CircleEvaluation::new_canonical_ordered(trace_domain, trace)
}
- pub fn prove(&self) -> StarkProof {
+ pub fn prove(&self) -> Result {
let trace = self.get_trace();
let channel =
&mut Blake2sChannel::new(Blake2sHasher::hash(BaseField::into_slice(&[self.claim])));
@@ -82,7 +82,8 @@ mod tests {
use crate::core::poly::circle::CanonicCoset;
use crate::core::prover::{prove, verify};
use crate::core::queries::Queries;
- use crate::core::utils::{bit_reverse, secure_eval_to_base_eval};
+ use crate::core::test_utils::secure_eval_to_base_eval;
+ use crate::core::utils::bit_reverse;
use crate::fibonacci::air::MultiFibonacciAir;
use crate::fibonacci::verify_proof;
use crate::{m31, qm31};
@@ -126,7 +127,7 @@ mod tests {
const FIB_LOG_SIZE: u32 = 5;
let fib = Fibonacci::new(FIB_LOG_SIZE, m31!(443693538));
- let proof = fib.prove();
+ let proof = fib.prove().unwrap();
let (composition_polynomial_quotient, trace_quotients) = proof
.additional_proof_data
.oods_quotients
@@ -187,7 +188,7 @@ mod tests {
let trace_poly = trace.interpolate();
let trace = ComponentTrace::new(vec![&trace_poly]);
- let proof = fib.prove();
+ let proof = fib.prove().unwrap();
let oods_point = proof.additional_proof_data.oods_point;
let (_, mask_values) = fib.air.component.mask_points_and_values(oods_point, &trace);
@@ -221,7 +222,7 @@ mod tests {
const FIB_LOG_SIZE: u32 = 5;
let fib = Fibonacci::new(FIB_LOG_SIZE, m31!(443693538));
- let mut invalid_proof = fib.prove();
+ let mut invalid_proof = fib.prove().unwrap();
invalid_proof.opened_values.0[0][0][4] += BaseField::one();
verify_proof::(invalid_proof, fib.claim);
@@ -235,7 +236,7 @@ mod tests {
const FIB_LOG_SIZE: u32 = 5;
let fib = Fibonacci::new(FIB_LOG_SIZE, m31!(443693538));
- let mut invalid_proof = fib.prove();
+ let mut invalid_proof = fib.prove().unwrap();
invalid_proof.trace_oods_values.swap(0, 1);
verify_proof::(invalid_proof, fib.claim);
@@ -249,7 +250,7 @@ mod tests {
const FIB_LOG_SIZE: u32 = 5;
let fib = Fibonacci::new(FIB_LOG_SIZE, m31!(443693538));
- let mut invalid_proof = fib.prove();
+ let mut invalid_proof = fib.prove().unwrap();
invalid_proof.opened_values.0[0][0].pop();
verify_proof::(invalid_proof, fib.claim);
@@ -263,7 +264,7 @@ mod tests {
let prover_channel =
&mut Blake2sChannel::new(Blake2sHasher::hash(BaseField::into_slice(&[fib.claim])));
let trace = vec![fib.get_trace(); n_components];
- let proof = prove(&air, prover_channel, trace);
+ let proof = prove(&air, prover_channel, trace).unwrap();
let verifier_channel =
&mut Blake2sChannel::new(Blake2sHasher::hash(BaseField::into_slice(&[fib.claim])));
assert!(verify(proof, &air, verifier_channel));