diff --git a/crates/prover/src/core/mod.rs b/crates/prover/src/core/mod.rs index 256ce9067..0157a5d99 100644 --- a/crates/prover/src/core/mod.rs +++ b/crates/prover/src/core/mod.rs @@ -86,7 +86,7 @@ impl Index<&str> for InteractionElements { } #[derive(Default, Debug)] -pub struct LookupValues(BTreeMap); +pub struct LookupValues(pub BTreeMap); impl LookupValues { pub fn new(values: BTreeMap) -> Self { diff --git a/crates/prover/src/core/prover/mod.rs b/crates/prover/src/core/prover/mod.rs index 91ff7288e..3113fe828 100644 --- a/crates/prover/src/core/prover/mod.rs +++ b/crates/prover/src/core/prover/mod.rs @@ -7,7 +7,7 @@ use super::backend::Backend; use super::fields::secure_column::SECURE_EXTENSION_DEGREE; use super::fri::FriVerificationError; use super::pcs::{CommitmentSchemeProof, TreeVec}; -use super::poly::circle::{CanonicCoset, MAX_CIRCLE_DOMAIN_LOG_SIZE}; +use super::poly::circle::MAX_CIRCLE_DOMAIN_LOG_SIZE; use super::poly::twiddles::TwiddleTree; use super::proof_of_work::ProofOfWorkVerificationError; use super::{ColumnVec, InteractionElements, LookupValues}; @@ -15,7 +15,6 @@ use crate::core::air::{Air, AirExt, AirProverExt}; use crate::core::backend::CpuBackend; use crate::core::channel::{Blake2sChannel, Channel as ChannelTrait}; use crate::core::circle::CirclePoint; -use crate::core::fields::m31::BaseField; use crate::core::fields::qm31::SecureField; use crate::core::pcs::{CommitmentSchemeProver, CommitmentSchemeVerifier}; use crate::core::poly::circle::CircleEvaluation; @@ -25,7 +24,6 @@ use crate::core::vcs::blake2_merkle::Blake2sMerkleHasher; use crate::core::vcs::hasher::Hasher; use crate::core::vcs::ops::MerkleOps; use crate::core::vcs::verifier::MerkleVerificationError; -use crate::trace_generation::{AirTraceGenerator, AirTraceVerifier}; type Channel = Blake2sChannel; type ChannelHasher = Blake2sHasher; @@ -36,9 +34,6 @@ pub const LOG_LAST_LAYER_DEGREE_BOUND: u32 = 0; pub const PROOF_OF_WORK_BITS: u32 = 12; pub const N_QUERIES: usize = 3; -pub const BASE_TRACE: usize = 0; -pub const INTERACTION_TRACE: usize = 1; - #[derive(Debug)] pub struct StarkProof { pub commitments: TreeVec<::Hash>, @@ -54,42 +49,7 @@ pub struct AdditionalProofData { pub oods_quotients: Vec>, } -pub fn evaluate_and_commit_on_trace>( - air: &impl AirTraceGenerator, - channel: &mut Channel, - twiddles: &TwiddleTree, - trace: ColumnVec>, -) -> Result<(CommitmentSchemeProver, InteractionElements), ProvingError> { - let span = span!(Level::INFO, "Trace interpolation").entered(); - // TODO(AlonH): Clone only the columns needed for interaction. - let trace_polys = trace - .clone() - .into_iter() - .map(|eval| eval.interpolate_with_twiddles(twiddles)) - .collect(); - span.exit(); - - let mut commitment_scheme = CommitmentSchemeProver::new(LOG_BLOWUP_FACTOR); - let span = span!(Level::INFO, "Trace commitment").entered(); - commitment_scheme.commit(trace_polys, channel, twiddles); - span.exit(); - - let interaction_elements = air.interaction_elements(channel); - let interaction_trace = air.interact(&trace, &interaction_elements); - if !interaction_elements.is_empty() { - let span = span!(Level::INFO, "Interaction trace interpolation").entered(); - let interaction_trace_polys = interaction_trace - .into_iter() - .map(|eval| eval.interpolate_with_twiddles(twiddles)) - .collect(); - span.exit(); - commitment_scheme.commit(interaction_trace_polys, channel, twiddles); - } - - Ok((commitment_scheme, interaction_elements)) -} - -pub fn prove>( +pub fn prove_without_commit>( air: &impl AirProver, channel: &mut Channel, interaction_elements: &InteractionElements, @@ -148,103 +108,7 @@ pub fn prove>( }) } -pub fn commit_and_prove>( - air: &impl AirTraceGenerator, - channel: &mut Channel, - trace: ColumnVec>, -) -> 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. - // TODO(AlonH): Get traces log degree bounds from trace writer. - let composition_polynomial_log_degree_bound = air.composition_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, - }); - } - - let span = span!(Level::INFO, "Precompute twiddle").entered(); - let twiddles = B::precompute_twiddles( - CanonicCoset::new(composition_polynomial_log_degree_bound + LOG_BLOWUP_FACTOR) - .circle_domain() - .half_coset, - ); - span.exit(); - - let (mut commitment_scheme, interaction_elements) = - evaluate_and_commit_on_trace(air, channel, &twiddles, trace)?; - - let air = air.to_air_prover(); - channel.mix_felts( - &air.lookup_values(&air.component_traces(&commitment_scheme.trees)) - .0 - .values() - .map(|v| SecureField::from(*v)) - .collect_vec(), - ); - - prove( - &air, - channel, - &interaction_elements, - &twiddles, - &mut commitment_scheme, - ) -} - -pub fn commit_and_verify( - proof: StarkProof, - air: &(impl Air + AirTraceVerifier), - channel: &mut Channel, -) -> Result<(), VerificationError> { - // Read trace commitment. - let mut commitment_scheme = CommitmentSchemeVerifier::new(); - - // TODO(spapini): Retrieve column_log_sizes from AirTraceVerifier, and remove the dependency on - // Air. - let column_log_sizes = air.column_log_sizes(); - commitment_scheme.commit( - proof.commitments[BASE_TRACE], - &column_log_sizes[BASE_TRACE], - channel, - ); - let interaction_elements = air.interaction_elements(channel); - - if !interaction_elements.is_empty() { - commitment_scheme.commit( - proof.commitments[INTERACTION_TRACE], - &column_log_sizes[INTERACTION_TRACE], - channel, - ); - } - - channel.mix_felts( - &proof - .lookup_values - .0 - .values() - .map(|v| SecureField::from(*v)) - .collect_vec(), - ); - verify( - air, - channel, - &interaction_elements, - &mut commitment_scheme, - proof, - ) -} - -pub fn verify( +pub fn verify_without_commit( air: &impl Air, channel: &mut Blake2sChannel, interaction_elements: &InteractionElements, @@ -363,220 +227,3 @@ pub enum VerificationError { #[error(transparent)] ProofOfWork(#[from] ProofOfWorkVerificationError), } - -#[cfg(test)] -mod tests { - use num_traits::Zero; - - use crate::core::air::accumulation::{DomainEvaluationAccumulator, PointEvaluationAccumulator}; - use crate::core::air::{Air, AirProver, Component, ComponentProver, ComponentTrace}; - use crate::core::backend::cpu::CpuCircleEvaluation; - use crate::core::backend::CpuBackend; - use crate::core::channel::Blake2sChannel; - use crate::core::circle::{CirclePoint, CirclePointIndex, Coset}; - use crate::core::fields::m31::BaseField; - use crate::core::fields::qm31::SecureField; - use crate::core::pcs::TreeVec; - use crate::core::poly::circle::{ - CanonicCoset, CircleDomain, CircleEvaluation, MAX_CIRCLE_DOMAIN_LOG_SIZE, - }; - use crate::core::poly::BitReversedOrder; - use crate::core::prover::{commit_and_prove, ProvingError}; - use crate::core::test_utils::test_channel; - use crate::core::{ColumnVec, InteractionElements, LookupValues}; - use crate::qm31; - use crate::trace_generation::registry::ComponentGenerationRegistry; - use crate::trace_generation::{AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator}; - - #[derive(Clone)] - struct TestAir> { - component: C, - } - - impl Air for TestAir { - fn components(&self) -> Vec<&dyn Component> { - vec![&self.component] - } - } - - impl AirTraceVerifier for TestAir { - fn interaction_elements(&self, _channel: &mut Blake2sChannel) -> InteractionElements { - InteractionElements::default() - } - } - - impl AirTraceGenerator for TestAir { - fn interact( - &self, - _trace: &ColumnVec>, - _elements: &InteractionElements, - ) -> Vec> { - vec![] - } - - fn to_air_prover(&self) -> impl AirProver { - self.clone() - } - - fn composition_log_degree_bound(&self) -> u32 { - self.component.max_constraint_log_degree_bound() - } - } - - impl AirProver for TestAir { - fn prover_components(&self) -> Vec<&dyn ComponentProver> { - vec![&self.component] - } - } - - #[derive(Clone)] - struct TestComponent { - log_size: u32, - max_constraint_log_degree_bound: u32, - } - - impl Component for TestComponent { - fn n_constraints(&self) -> usize { - 0 - } - - fn max_constraint_log_degree_bound(&self) -> u32 { - self.max_constraint_log_degree_bound - } - - fn trace_log_degree_bounds(&self) -> TreeVec> { - TreeVec::new(vec![vec![self.log_size]]) - } - - fn mask_points( - &self, - point: CirclePoint, - ) -> TreeVec>>> { - TreeVec::new(vec![vec![vec![point]]]) - } - - fn evaluate_constraint_quotients_at_point( - &self, - _point: CirclePoint, - _mask: &TreeVec>>, - evaluation_accumulator: &mut PointEvaluationAccumulator, - _interaction_elements: &InteractionElements, - _lookup_values: &LookupValues, - ) { - evaluation_accumulator.accumulate(qm31!(0, 0, 0, 1)) - } - } - - impl ComponentTraceGenerator for TestComponent { - type Component = Self; - type Inputs = (); - - fn add_inputs(&mut self, _inputs: &Self::Inputs) {} - - fn write_trace( - _component_id: &str, - _registry: &mut ComponentGenerationRegistry, - ) -> ColumnVec> { - vec![] - } - - fn write_interaction_trace( - &self, - _trace: &ColumnVec<&CircleEvaluation>, - _elements: &InteractionElements, - ) -> ColumnVec> { - vec![] - } - - fn component(&self) -> Self::Component { - self.clone() - } - } - - impl ComponentProver for TestComponent { - fn evaluate_constraint_quotients_on_domain( - &self, - _trace: &ComponentTrace<'_, CpuBackend>, - _evaluation_accumulator: &mut DomainEvaluationAccumulator, - _interaction_elements: &InteractionElements, - _lookup_values: &LookupValues, - ) { - // Does nothing. - } - - fn lookup_values(&self, _trace: &ComponentTrace<'_, CpuBackend>) -> LookupValues { - LookupValues::default() - } - } - - // 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 { - component: TestComponent { - log_size: LOG_DOMAIN_SIZE, - 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 = commit_and_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; - const LOG_DOMAIN_SIZE: u32 = 5; - let air = TestAir { - component: TestComponent { - log_size: LOG_DOMAIN_SIZE, - max_constraint_log_degree_bound: COMPOSITION_POLYNOMIAL_DEGREE, - }, - }; - 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 = commit_and_prove(&air, &mut test_channel(), trace).unwrap_err(); - assert!(matches!( - proof_error, - ProvingError::MaxCompositionDegreeExceeded { - degree: COMPOSITION_POLYNOMIAL_DEGREE - } - )); - } - - #[test] - fn test_constraints_not_satisfied() { - const LOG_DOMAIN_SIZE: u32 = 5; - let air = TestAir { - component: TestComponent { - log_size: LOG_DOMAIN_SIZE, - max_constraint_log_degree_bound: LOG_DOMAIN_SIZE + 1, - }, - }; - let domain = CanonicCoset::new(LOG_DOMAIN_SIZE).circle_domain(); - let values = vec![BaseField::zero(); 1 << LOG_DOMAIN_SIZE]; - let trace = vec![CpuCircleEvaluation::new(domain, values)]; - - let proof = commit_and_prove(&air, &mut test_channel(), trace).unwrap_err(); - assert!(matches!(proof, ProvingError::ConstraintsNotSatisfied)); - } -} diff --git a/crates/prover/src/examples/fibonacci/component.rs b/crates/prover/src/examples/fibonacci/component.rs index e756cc517..6eb87b51d 100644 --- a/crates/prover/src/examples/fibonacci/component.rs +++ b/crates/prover/src/examples/fibonacci/component.rs @@ -14,11 +14,10 @@ use crate::core::fields::{ExtensionOf, FieldExpOps}; use crate::core::pcs::TreeVec; use crate::core::poly::circle::{CanonicCoset, CircleEvaluation}; use crate::core::poly::BitReversedOrder; -use crate::core::prover::BASE_TRACE; use crate::core::utils::bit_reverse_index; use crate::core::{ColumnVec, InteractionElements, LookupValues}; use crate::trace_generation::registry::ComponentGenerationRegistry; -use crate::trace_generation::{ComponentGen, ComponentTraceGenerator}; +use crate::trace_generation::{ComponentGen, ComponentTraceGenerator, BASE_TRACE}; #[derive(Clone)] pub struct FibonacciComponent { diff --git a/crates/prover/src/examples/fibonacci/mod.rs b/crates/prover/src/examples/fibonacci/mod.rs index c3d7aec09..3dd47fe12 100644 --- a/crates/prover/src/examples/fibonacci/mod.rs +++ b/crates/prover/src/examples/fibonacci/mod.rs @@ -10,11 +10,10 @@ 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::{ - commit_and_prove, commit_and_verify, ProvingError, StarkProof, VerificationError, -}; +use crate::core::prover::{ProvingError, StarkProof, VerificationError}; use crate::core::vcs::blake2_hash::Blake2sHasher; use crate::core::vcs::hasher::Hasher; +use crate::trace_generation::{commit_and_prove, commit_and_verify}; pub mod air; mod component; @@ -130,7 +129,7 @@ mod tests { use crate::core::fields::IntoSlice; use crate::core::pcs::TreeVec; use crate::core::poly::circle::CanonicCoset; - use crate::core::prover::{commit_and_prove, commit_and_verify, VerificationError, BASE_TRACE}; + use crate::core::prover::VerificationError; use crate::core::queries::Queries; use crate::core::utils::bit_reverse; use crate::core::vcs::blake2_hash::Blake2sHasher; @@ -138,7 +137,9 @@ mod tests { use crate::core::{InteractionElements, LookupValues}; use crate::examples::fibonacci::air::FibonacciAirGenerator; use crate::examples::fibonacci::component::FibonacciInput; - use crate::trace_generation::AirTraceGenerator; + use crate::trace_generation::{ + commit_and_prove, commit_and_verify, AirTraceGenerator, BASE_TRACE, + }; use crate::{m31, qm31}; pub fn generate_test_queries(n_queries: usize, trace_length: usize) -> Vec { diff --git a/crates/prover/src/examples/poseidon/mod.rs b/crates/prover/src/examples/poseidon/mod.rs index 4eedc6df5..869c321e1 100644 --- a/crates/prover/src/examples/poseidon/mod.rs +++ b/crates/prover/src/examples/poseidon/mod.rs @@ -496,13 +496,13 @@ mod tests { use crate::core::channel::{Blake2sChannel, Channel}; use crate::core::fields::m31::BaseField; use crate::core::fields::IntoSlice; - use crate::core::prover::{commit_and_prove, commit_and_verify}; use crate::core::vcs::blake2_hash::Blake2sHasher; use crate::core::vcs::hasher::Hasher; use crate::examples::poseidon::{ apply_internal_round_matrix, apply_m4, gen_trace, PoseidonAir, PoseidonComponent, }; use crate::math::matrix::{RowMajorMatrix, SquareMatrix}; + use crate::trace_generation::{commit_and_prove, commit_and_verify}; #[test] fn test_apply_m4() { diff --git a/crates/prover/src/examples/wide_fibonacci/constraint_eval.rs b/crates/prover/src/examples/wide_fibonacci/constraint_eval.rs index 6b1f6adcd..79a028732 100644 --- a/crates/prover/src/examples/wide_fibonacci/constraint_eval.rs +++ b/crates/prover/src/examples/wide_fibonacci/constraint_eval.rs @@ -20,14 +20,15 @@ use crate::core::fields::FieldExpOps; use crate::core::pcs::TreeVec; use crate::core::poly::circle::{CanonicCoset, CircleDomain, CircleEvaluation}; use crate::core::poly::BitReversedOrder; -use crate::core::prover::{BASE_TRACE, INTERACTION_TRACE}; use crate::core::utils::{ bit_reverse, point_vanish_denominator_inverses, previous_bit_reversed_circle_domain_index, shifted_secure_combination, }; use crate::core::{ColumnVec, InteractionElements, LookupValues}; use crate::examples::wide_fibonacci::component::LOG_N_COLUMNS; -use crate::trace_generation::{AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator}; +use crate::trace_generation::{ + AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator, BASE_TRACE, INTERACTION_TRACE, +}; // TODO(AlonH): Rename file to `cpu.rs`. diff --git a/crates/prover/src/examples/wide_fibonacci/mod.rs b/crates/prover/src/examples/wide_fibonacci/mod.rs index ef6fc186d..b28b3dd48 100644 --- a/crates/prover/src/examples/wide_fibonacci/mod.rs +++ b/crates/prover/src/examples/wide_fibonacci/mod.rs @@ -22,7 +22,6 @@ mod tests { use crate::core::fields::IntoSlice; use crate::core::pcs::TreeVec; use crate::core::poly::circle::CanonicCoset; - use crate::core::prover::{commit_and_verify, commit_and_prove}; use crate::core::utils::{ bit_reverse, circle_domain_order_to_coset_order, shifted_secure_combination, }; @@ -30,7 +29,7 @@ mod tests { use crate::core::vcs::hasher::Hasher; use crate::core::InteractionElements; use crate::examples::wide_fibonacci::trace_gen::write_lookup_column; - use crate::trace_generation::ComponentTraceGenerator; + use crate::trace_generation::{commit_and_prove, commit_and_verify, ComponentTraceGenerator}; use crate::{m31, qm31}; pub fn assert_constraints_on_row(row: &[BaseField]) { diff --git a/crates/prover/src/examples/wide_fibonacci/simd.rs b/crates/prover/src/examples/wide_fibonacci/simd.rs index d690d9b58..e43623bf5 100644 --- a/crates/prover/src/examples/wide_fibonacci/simd.rs +++ b/crates/prover/src/examples/wide_fibonacci/simd.rs @@ -20,11 +20,12 @@ use crate::core::fields::{FieldExpOps, FieldOps}; use crate::core::pcs::TreeVec; use crate::core::poly::circle::{CanonicCoset, CircleEvaluation}; use crate::core::poly::BitReversedOrder; -use crate::core::prover::BASE_TRACE; use crate::core::{ColumnVec, InteractionElements, LookupValues}; use crate::examples::wide_fibonacci::component::N_COLUMNS; use crate::trace_generation::registry::ComponentGenerationRegistry; -use crate::trace_generation::{AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator}; +use crate::trace_generation::{ + AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator, BASE_TRACE, +}; // TODO(AlonH): Remove this once the Cpu and Simd implementations are aligned. #[derive(Clone)] @@ -257,11 +258,11 @@ mod tests { use crate::core::channel::{Blake2sChannel, Channel}; use crate::core::fields::m31::BaseField; use crate::core::fields::IntoSlice; - use crate::core::prover::{commit_and_prove, commit_and_verify}; use crate::core::vcs::blake2_hash::Blake2sHasher; use crate::core::vcs::hasher::Hasher; use crate::examples::wide_fibonacci::component::LOG_N_COLUMNS; use crate::examples::wide_fibonacci::simd::{gen_trace, SimdWideFibAir, SimdWideFibComponent}; + use crate::trace_generation::{commit_and_prove, commit_and_verify}; #[test_log::test] fn test_simd_wide_fib_prove() { diff --git a/crates/prover/src/trace_generation/mod.rs b/crates/prover/src/trace_generation/mod.rs index 7019cc94f..53bfeadb3 100644 --- a/crates/prover/src/trace_generation/mod.rs +++ b/crates/prover/src/trace_generation/mod.rs @@ -1,16 +1,33 @@ pub mod registry; use downcast_rs::{impl_downcast, Downcast}; +use itertools::Itertools; use registry::ComponentGenerationRegistry; +use tracing::{span, Level}; -use crate::core::air::{AirProver, Component}; +use crate::core::air::{Air, AirExt, AirProver, AirProverExt, Component}; use crate::core::backend::Backend; -use crate::core::channel::Blake2sChannel; +use crate::core::channel::{Blake2sChannel, Channel as _}; use crate::core::fields::m31::BaseField; -use crate::core::poly::circle::CircleEvaluation; +use crate::core::fields::qm31::SecureField; +use crate::core::pcs::{CommitmentSchemeProver, CommitmentSchemeVerifier}; +use crate::core::poly::circle::{CanonicCoset, CircleEvaluation, MAX_CIRCLE_DOMAIN_LOG_SIZE}; +use crate::core::poly::twiddles::TwiddleTree; use crate::core::poly::BitReversedOrder; +use crate::core::prover::{ + prove_without_commit, verify_without_commit, ProvingError, StarkProof, VerificationError, + LOG_BLOWUP_FACTOR, +}; +use crate::core::vcs::blake2_merkle::Blake2sMerkleHasher; +use crate::core::vcs::ops::MerkleOps; use crate::core::{ColumnVec, InteractionElements}; +pub const BASE_TRACE: usize = 0; +pub const INTERACTION_TRACE: usize = 1; + +type MerkleHasher = Blake2sMerkleHasher; +type Channel = Blake2sChannel; + pub trait ComponentGen: Downcast {} impl_downcast!(ComponentGen); @@ -64,3 +81,354 @@ pub trait AirTraceGenerator: AirTraceVerifier { fn to_air_prover(&self) -> impl AirProver; } + +pub fn commit_and_prove>( + air: &impl AirTraceGenerator, + channel: &mut Channel, + trace: ColumnVec>, +) -> 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. + // TODO(AlonH): Get traces log degree bounds from trace writer. + let composition_polynomial_log_degree_bound = air.composition_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, + }); + } + + let span = span!(Level::INFO, "Precompute twiddle").entered(); + let composition_polynomial_log_degree_bound = air.composition_log_degree_bound(); + let twiddles = B::precompute_twiddles( + CanonicCoset::new(composition_polynomial_log_degree_bound + LOG_BLOWUP_FACTOR) + .circle_domain() + .half_coset, + ); + span.exit(); + + let (mut commitment_scheme, interaction_elements) = + evaluate_and_commit_on_trace(air, channel, &twiddles, trace)?; + + let air = air.to_air_prover(); + channel.mix_felts( + &air.lookup_values(&air.component_traces(&commitment_scheme.trees)) + .0 + .values() + .map(|v| SecureField::from(*v)) + .collect_vec(), + ); + + prove_without_commit( + &air, + channel, + &interaction_elements, + &twiddles, + &mut commitment_scheme, + ) +} + +pub fn evaluate_and_commit_on_trace>( + air: &impl AirTraceGenerator, + channel: &mut Channel, + twiddles: &TwiddleTree, + trace: ColumnVec>, +) -> Result<(CommitmentSchemeProver, InteractionElements), ProvingError> { + let span = span!(Level::INFO, "Trace interpolation").entered(); + // TODO(AlonH): Clone only the columns needed for interaction. + let trace_polys = trace + .clone() + .into_iter() + .map(|eval| eval.interpolate_with_twiddles(twiddles)) + .collect(); + span.exit(); + + let mut commitment_scheme = CommitmentSchemeProver::new(LOG_BLOWUP_FACTOR); + let span = span!(Level::INFO, "Trace commitment").entered(); + commitment_scheme.commit(trace_polys, channel, twiddles); + span.exit(); + + let interaction_elements = air.interaction_elements(channel); + let interaction_trace = air.interact(&trace, &interaction_elements); + if !interaction_elements.is_empty() { + let span = span!(Level::INFO, "Interaction trace interpolation").entered(); + let interaction_trace_polys = interaction_trace + .into_iter() + .map(|eval| eval.interpolate_with_twiddles(twiddles)) + .collect(); + span.exit(); + commitment_scheme.commit(interaction_trace_polys, channel, twiddles); + } + + Ok((commitment_scheme, interaction_elements)) +} + +pub fn commit_and_verify( + proof: StarkProof, + air: &(impl Air + AirTraceVerifier), + channel: &mut Channel, +) -> Result<(), VerificationError> { + // Read trace commitment. + let mut commitment_scheme = CommitmentSchemeVerifier::new(); + + // TODO(spapini): Retrieve column_log_sizes from AirTraceVerifier, and remove the dependency on + // Air. + let column_log_sizes = air.column_log_sizes(); + commitment_scheme.commit( + proof.commitments[BASE_TRACE], + &column_log_sizes[BASE_TRACE], + channel, + ); + let interaction_elements = air.interaction_elements(channel); + + if !interaction_elements.is_empty() { + commitment_scheme.commit( + proof.commitments[INTERACTION_TRACE], + &column_log_sizes[INTERACTION_TRACE], + channel, + ); + } + + channel.mix_felts( + &proof + .lookup_values + .0 + .values() + .map(|v| SecureField::from(*v)) + .collect_vec(), + ); + verify_without_commit( + air, + channel, + &interaction_elements, + &mut commitment_scheme, + proof, + ) +} + +#[cfg(test)] +mod tests { + use num_traits::Zero; + + use crate::core::air::accumulation::{DomainEvaluationAccumulator, PointEvaluationAccumulator}; + use crate::core::air::{Air, AirProver, Component, ComponentProver, ComponentTrace}; + use crate::core::backend::cpu::CpuCircleEvaluation; + use crate::core::backend::CpuBackend; + use crate::core::channel::Blake2sChannel; + use crate::core::circle::{CirclePoint, CirclePointIndex, Coset}; + use crate::core::fields::m31::BaseField; + use crate::core::fields::qm31::SecureField; + use crate::core::pcs::TreeVec; + use crate::core::poly::circle::{ + CanonicCoset, CircleDomain, CircleEvaluation, MAX_CIRCLE_DOMAIN_LOG_SIZE, + }; + use crate::core::poly::BitReversedOrder; + use crate::core::prover::ProvingError; + use crate::core::test_utils::test_channel; + use crate::core::{ColumnVec, InteractionElements, LookupValues}; + use crate::qm31; + use crate::trace_generation::registry::ComponentGenerationRegistry; + use crate::trace_generation::{ + commit_and_prove, AirTraceGenerator, AirTraceVerifier, ComponentTraceGenerator, + }; + + #[derive(Clone)] + struct TestAir> { + component: C, + } + + impl Air for TestAir { + fn components(&self) -> Vec<&dyn Component> { + vec![&self.component] + } + } + + impl AirTraceVerifier for TestAir { + fn interaction_elements(&self, _channel: &mut Blake2sChannel) -> InteractionElements { + InteractionElements::default() + } + } + + impl AirTraceGenerator for TestAir { + fn interact( + &self, + _trace: &ColumnVec>, + _elements: &InteractionElements, + ) -> Vec> { + vec![] + } + + fn to_air_prover(&self) -> impl AirProver { + self.clone() + } + + fn composition_log_degree_bound(&self) -> u32 { + self.component.max_constraint_log_degree_bound() + } + } + + impl AirProver for TestAir { + fn prover_components(&self) -> Vec<&dyn ComponentProver> { + vec![&self.component] + } + } + + #[derive(Clone)] + struct TestComponent { + log_size: u32, + max_constraint_log_degree_bound: u32, + } + + impl Component for TestComponent { + fn n_constraints(&self) -> usize { + 0 + } + + fn max_constraint_log_degree_bound(&self) -> u32 { + self.max_constraint_log_degree_bound + } + + fn trace_log_degree_bounds(&self) -> TreeVec> { + TreeVec::new(vec![vec![self.log_size]]) + } + + fn mask_points( + &self, + point: CirclePoint, + ) -> TreeVec>>> { + TreeVec::new(vec![vec![vec![point]]]) + } + + fn evaluate_constraint_quotients_at_point( + &self, + _point: CirclePoint, + _mask: &TreeVec>>, + evaluation_accumulator: &mut PointEvaluationAccumulator, + _interaction_elements: &InteractionElements, + _lookup_values: &LookupValues, + ) { + evaluation_accumulator.accumulate(qm31!(0, 0, 0, 1)) + } + } + + impl ComponentTraceGenerator for TestComponent { + type Component = Self; + type Inputs = (); + + fn add_inputs(&mut self, _inputs: &Self::Inputs) {} + + fn write_trace( + _component_id: &str, + _registry: &mut ComponentGenerationRegistry, + ) -> ColumnVec> { + vec![] + } + + fn write_interaction_trace( + &self, + _trace: &ColumnVec<&CircleEvaluation>, + _elements: &InteractionElements, + ) -> ColumnVec> { + vec![] + } + + fn component(&self) -> Self::Component { + self.clone() + } + } + + impl ComponentProver for TestComponent { + fn evaluate_constraint_quotients_on_domain( + &self, + _trace: &ComponentTrace<'_, CpuBackend>, + _evaluation_accumulator: &mut DomainEvaluationAccumulator, + _interaction_elements: &InteractionElements, + _lookup_values: &LookupValues, + ) { + // Does nothing. + } + + fn lookup_values(&self, _trace: &ComponentTrace<'_, CpuBackend>) -> LookupValues { + LookupValues::default() + } + } + + // 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 { + component: TestComponent { + log_size: LOG_DOMAIN_SIZE, + 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 = commit_and_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; + const LOG_DOMAIN_SIZE: u32 = 5; + let air = TestAir { + component: TestComponent { + log_size: LOG_DOMAIN_SIZE, + max_constraint_log_degree_bound: COMPOSITION_POLYNOMIAL_DEGREE, + }, + }; + 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 = commit_and_prove(&air, &mut test_channel(), trace).unwrap_err(); + assert!(matches!( + proof_error, + ProvingError::MaxCompositionDegreeExceeded { + degree: COMPOSITION_POLYNOMIAL_DEGREE + } + )); + } + + #[test] + fn test_constraints_not_satisfied() { + const LOG_DOMAIN_SIZE: u32 = 5; + let air = TestAir { + component: TestComponent { + log_size: LOG_DOMAIN_SIZE, + max_constraint_log_degree_bound: LOG_DOMAIN_SIZE + 1, + }, + }; + let domain = CanonicCoset::new(LOG_DOMAIN_SIZE).circle_domain(); + let values = vec![BaseField::zero(); 1 << LOG_DOMAIN_SIZE]; + let trace = vec![CpuCircleEvaluation::new(domain, values)]; + + let proof = commit_and_prove(&air, &mut test_channel(), trace).unwrap_err(); + assert!(matches!(proof, ProvingError::ConstraintsNotSatisfied)); + } +}