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));