diff --git a/crates/prover/src/core/fri.rs b/crates/prover/src/core/fri.rs index 5f51434c0..c9ece444f 100644 --- a/crates/prover/src/core/fri.rs +++ b/crates/prover/src/core/fri.rs @@ -423,7 +423,7 @@ impl FriVerifier { /// Verifies the decommitment stage of FRI. /// - /// The decommitment values need to be provided in the same order as their commitment. + /// The query evals need to be provided in the same order as their commitment. /// /// # Panics /// @@ -550,10 +550,9 @@ impl FriVerifier { } } -/// Returns the column query positions needed for verification. +/// Returns the column query positions mapped by sample domain log size. /// -/// The column log sizes must be unique and in descending order. -/// Returned column query positions are mapped by their log size. +/// Query positions are in ascending order. fn get_query_positions_by_log_size( queries: &Queries, column_log_sizes: BTreeSet, @@ -654,7 +653,7 @@ pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1; /// Proof of an individual FRI layer. #[derive(Debug, Serialize, Deserialize)] pub struct FriLayerProof { - /// values that the verifier needs but cannot deduce from previous computations, in the + /// Values that the verifier needs but cannot deduce from previous computations, in the /// order they are needed. This complements the values that were queried. These must be /// supplied directly to the verifier. pub fri_witness: Vec, @@ -688,7 +687,7 @@ impl FriFirstLayerVerifier { fn verify_and_fold( &self, queries: &Queries, - evals_at_queries_by_column: ColumnVec>, + query_evals_by_column: ColumnVec>, ) -> Result<(Queries, ColumnVec>), FriVerificationError> { // Columns are provided in descending order by size. let max_column_log_size = self.column_commitment_domains[0].log_size(); @@ -699,18 +698,17 @@ impl FriFirstLayerVerifier { let mut all_column_decommitment_values = Vec::new(); let mut folded_evals_by_column = Vec::new(); - for (&column_domain, column_evals_at_queries) in - zip_eq(&self.column_commitment_domains, evals_at_queries_by_column) + for (&column_domain, column_query_evals) in + zip_eq(&self.column_commitment_domains, query_evals_by_column) { let column_queries = queries.fold(queries.log_domain_size - column_domain.log_size()); let (column_decommitment_positions, sparse_evaluation) = compute_decommitment_positions_and_rebuild_evals( - &column_queries.positions, - &column_evals_at_queries, + &column_queries, + &column_query_evals, &mut fri_witness, CIRCLE_TO_LINE_FOLD_STEP, - column_domain.log_size(), ) .map_err(|InsufficientWitnessError| { FriVerificationError::FirstLayerEvaluationsInvalid @@ -794,11 +792,10 @@ impl FriInnerLayerVerifier { let (decommitment_positions, sparse_evaluation) = compute_decommitment_positions_and_rebuild_evals( - &queries.positions, + &queries, &evals_at_queries, &mut fri_witness, FOLD_STEP, - self.domain.log_size(), ) .map_err(|InsufficientWitnessError| { FriVerificationError::InnerLayerEvaluationsInvalid { @@ -1009,11 +1006,10 @@ fn compute_decommitment_positions_and_witness_evals( /// /// Panics if the number of queries doesn't match the number of query evals. fn compute_decommitment_positions_and_rebuild_evals( - queries: &[usize], + queries: &Queries, query_evals: &[QM31], mut witness_evals: impl Iterator, fold_step: u32, - column_log_size: u32, ) -> Result<(Vec, SparseEvaluation), InsufficientWitnessError> { let mut query_evals = query_evals.iter().copied(); @@ -1037,7 +1033,7 @@ fn compute_decommitment_positions_and_rebuild_evals( .collect::>()?; subset_evals.push(subset_eval); - subset_domain_index_initials.push(bit_reverse_index(subset_start, column_log_size)); + subset_domain_index_initials.push(bit_reverse_index(subset_start, queries.log_domain_size)); } let sparse_evaluation = SparseEvaluation::new(subset_evals, subset_domain_index_initials); diff --git a/crates/prover/src/core/pcs/quotients.rs b/crates/prover/src/core/pcs/quotients.rs index d3575a641..aca0901c6 100644 --- a/crates/prover/src/core/pcs/quotients.rs +++ b/crates/prover/src/core/pcs/quotients.rs @@ -131,7 +131,6 @@ pub fn fri_answers_for_log_size( query_positions: &[usize], queried_values_per_column: &[&Vec], ) -> Result, VerificationError> { - let sample_batches = ColumnSampleBatch::new_vec(samples); for queried_values in queried_values_per_column { if queried_values.len() != query_positions.len() { return Err(VerificationError::InvalidStructure( @@ -140,6 +139,7 @@ pub fn fri_answers_for_log_size( } } + let sample_batches = ColumnSampleBatch::new_vec(samples); let quotient_constants = quotient_constants(&sample_batches, random_coeff); let commitment_domain = CanonicCoset::new(log_size).circle_domain(); let mut quotient_evals_at_queries = Vec::new(); diff --git a/crates/prover/src/core/pcs/verifier.rs b/crates/prover/src/core/pcs/verifier.rs index 80140892d..200fe98d5 100644 --- a/crates/prover/src/core/pcs/verifier.rs +++ b/crates/prover/src/core/pcs/verifier.rs @@ -83,7 +83,7 @@ impl CommitmentSchemeVerifier { return Err(VerificationError::ProofOfWork); } - // Get FRI query domains. + // Get FRI query positions. let query_positions_per_log_size = fri_verifier.sample_query_positions(channel); // Verify merkle decommitments. diff --git a/crates/prover/src/core/poly/line.rs b/crates/prover/src/core/poly/line.rs index 2bf640c63..b7c288963 100644 --- a/crates/prover/src/core/poly/line.rs +++ b/crates/prover/src/core/poly/line.rs @@ -114,9 +114,9 @@ pub struct LinePoly { /// /// The coefficients are stored in bit-reversed order. #[allow(rustdoc::private_intra_doc_links)] - coeffs: Vec, + pub coeffs: Vec, /// The number of coefficients stored as `log2(len(coeffs))`. - log_size: u32, + pub log_size: u32, } impl LinePoly { diff --git a/crates/prover/src/examples/blake/air.rs b/crates/prover/src/examples/blake/air.rs index 2db5d95af..7e06f0f63 100644 --- a/crates/prover/src/examples/blake/air.rs +++ b/crates/prover/src/examples/blake/air.rs @@ -529,6 +529,7 @@ pub fn verify_blake( mod tests { use std::env; + use crate::core::fri::FriConfig; use crate::core::pcs::PcsConfig; use crate::core::vcs::blake2_merkle::Blake2sMerkleChannel; use crate::examples::blake::air::{prove_blake, verify_blake}; @@ -547,7 +548,15 @@ mod tests { .unwrap_or_else(|_| "6".to_string()) .parse::() .unwrap(); - let config = PcsConfig::default(); + // let config = PcsConfig::default(); + let config = PcsConfig { + pow_bits: 10, + fri_config: FriConfig { + log_blowup_factor: 1, + log_last_layer_degree_bound: 6, + n_queries: 50, + }, + }; // Prove. let proof = prove_blake::(log_n_instances, config); diff --git a/crates/prover/src/examples/mod.rs b/crates/prover/src/examples/mod.rs index 4a3511b51..b35a5be8e 100644 --- a/crates/prover/src/examples/mod.rs +++ b/crates/prover/src/examples/mod.rs @@ -3,4 +3,4 @@ pub mod plonk; pub mod poseidon; pub mod state_machine; pub mod wide_fibonacci; -pub mod xor; +// pub mod xor; diff --git a/crates/prover/src/examples/wide_fibonacci/mod.rs b/crates/prover/src/examples/wide_fibonacci/mod.rs index afdc64caa..09de8c130 100644 --- a/crates/prover/src/examples/wide_fibonacci/mod.rs +++ b/crates/prover/src/examples/wide_fibonacci/mod.rs @@ -69,8 +69,12 @@ pub fn generate_trace( #[cfg(test)] mod tests { + use std::fs::File; + use std::io::Write; + use itertools::Itertools; use num_traits::{One, Zero}; + use starknet_ff::FieldElement; use super::WideFibonacciEval; use crate::constraint_framework::{ @@ -85,17 +89,23 @@ mod tests { use crate::core::channel::Poseidon252Channel; use crate::core::fields::m31::BaseField; use crate::core::fields::qm31::SecureField; - use crate::core::pcs::{CommitmentSchemeProver, CommitmentSchemeVerifier, PcsConfig, TreeVec}; + use crate::core::fri::{FriLayerProof, FriProof}; + use crate::core::pcs::{ + CommitmentSchemeProof, CommitmentSchemeProver, CommitmentSchemeVerifier, PcsConfig, TreeVec, + }; use crate::core::poly::circle::{CanonicCoset, CircleEvaluation, PolyOps}; + use crate::core::poly::line::LinePoly; use crate::core::poly::BitReversedOrder; - use crate::core::prover::{prove, verify}; + use crate::core::prover::{prove, verify, StarkProof}; use crate::core::vcs::blake2_merkle::Blake2sMerkleChannel; #[cfg(not(target_arch = "wasm32"))] use crate::core::vcs::poseidon252_merkle::Poseidon252MerkleChannel; + use crate::core::vcs::poseidon252_merkle::Poseidon252MerkleHasher; + use crate::core::vcs::prover::MerkleDecommitment; use crate::core::ColumnVec; use crate::examples::wide_fibonacci::{generate_trace, FibInput, WideFibonacciComponent}; - const FIB_SEQUENCE_LENGTH: usize = 100; + const FIB_SEQUENCE_LENGTH: usize = 512; fn generate_test_trace( log_n_instances: u32, @@ -153,7 +163,7 @@ mod tests { #[test] #[should_panic] fn test_wide_fibonacci_constraints_fails() { - const LOG_N_INSTANCES: u32 = 6; + const LOG_N_INSTANCES: u32 = 125; let mut trace = generate_test_trace(LOG_N_INSTANCES); // Modify the trace such that a constraint fail. @@ -229,8 +239,15 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_wide_fib_prove_with_poseidon() { - const LOG_N_INSTANCES: u32 = 6; - let config = PcsConfig::default(); + use std::time::Instant; + + use crate::core::fri::FriConfig; + + const LOG_N_INSTANCES: u32 = 20; + let config = PcsConfig { + pow_bits: 10, + fri_config: FriConfig::new(4, 4, 15), + }; // Precompute twiddles. let twiddles = SimdBackend::precompute_twiddles( CanonicCoset::new(LOG_N_INSTANCES + 1 + config.fri_config.log_blowup_factor) @@ -249,11 +266,18 @@ mod tests { tree_builder.extend_evals([]); tree_builder.commit(prover_channel); + let now = Instant::now(); // Trace. let trace = generate_test_trace(LOG_N_INSTANCES); + + println!("test trace took: {:?}", now.elapsed()); + + let now = Instant::now(); + let mut tree_builder = commitment_scheme.tree_builder(); tree_builder.extend_evals(trace); tree_builder.commit(prover_channel); + println!("extend took: {:?}", now.elapsed()); // Prove constraints. let component = WideFibonacciComponent::new( @@ -263,6 +287,7 @@ mod tests { }, (SecureField::zero(), None), ); + let now = Instant::now(); let proof = prove::( &[&component], prover_channel, @@ -270,6 +295,11 @@ mod tests { ) .unwrap(); + println!("proving took: {:?}", now.elapsed()); + + // _serialize_proof_cairo1(&proof); + _serialize_proof_cairo1_array(&proof); + // Verify. let verifier_channel = &mut Poseidon252Channel::default(); let commitment_scheme = @@ -278,7 +308,127 @@ mod tests { // Retrieve the expected column sizes in each commitment interaction, from the AIR. let sizes = component.trace_log_degree_bounds(); commitment_scheme.commit(proof.commitments[0], &sizes[0], verifier_channel); - commitment_scheme.commit(proof.commitments[1], &sizes[1], verifier_channel); verify(&[&component], verifier_channel, commitment_scheme, proof).unwrap(); } + + fn _serialize_proof_cairo1_array(proof: &StarkProof) { + trait CairoSerialize { + fn serialize(&self, buffer: &mut Vec); + } + + impl CairoSerialize for BaseField { + fn serialize(&self, buffer: &mut Vec) { + buffer.push(self.0.into()); + } + } + + impl CairoSerialize for SecureField { + fn serialize(&self, buffer: &mut Vec) { + buffer.extend(self.to_m31_array().map(|c| FieldElement::from(c.0))); + } + } + + impl CairoSerialize for MerkleDecommitment { + fn serialize(&self, buffer: &mut Vec) { + let Self { + hash_witness, + column_witness, + } = self; + hash_witness.serialize(buffer); + column_witness.serialize(buffer); + } + } + + impl CairoSerialize for LinePoly { + fn serialize(&self, buffer: &mut Vec) { + let Self { coeffs, log_size } = self; + coeffs.serialize(buffer); + buffer.push((*log_size).into()); + } + } + + impl CairoSerialize for FriLayerProof { + fn serialize(&self, buffer: &mut Vec) { + let Self { + fri_witness, + decommitment, + commitment, + } = self; + fri_witness.serialize(buffer); + decommitment.serialize(buffer); + commitment.serialize(buffer); + } + } + + impl CairoSerialize for FriProof { + fn serialize(&self, buffer: &mut Vec) { + let Self { + first_layer, + inner_layers, + last_layer_poly, + } = self; + first_layer.serialize(buffer); + inner_layers.serialize(buffer); + last_layer_poly.serialize(buffer); + } + } + + impl CairoSerialize for FieldElement { + fn serialize(&self, buffer: &mut Vec) { + buffer.push(*self); + } + } + + impl CairoSerialize for CommitmentSchemeProof { + fn serialize(&self, buffer: &mut Vec) { + let Self { + commitments, + sampled_values, + decommitments, + queried_values, + proof_of_work, + fri_proof, + } = self; + commitments.serialize(buffer); + sampled_values.serialize(buffer); + decommitments.serialize(buffer); + queried_values.serialize(buffer); + buffer.push((*proof_of_work).into()); + fri_proof.serialize(buffer); + } + } + + impl CairoSerialize for StarkProof { + fn serialize(&self, buffer: &mut Vec) { + let Self(commitment_scheme_proof) = self; + commitment_scheme_proof.serialize(buffer); + } + } + + impl CairoSerialize for Vec { + fn serialize(&self, buffer: &mut Vec) { + buffer.push(self.len().into()); + self.iter().for_each(|v| v.serialize(buffer)); + } + } + + let mut values = Vec::new(); + proof.serialize(&mut values); + + println!("proof size: {} field elements", values.len()); + + let ser_proof = format!( + r#" + use stwo_cairo_verifier::verifier::StarkProof; + + pub fn proof() -> StarkProof {{ + let mut proof_data = array![{}].span(); + Serde::deserialize(ref proof_data).unwrap() + }}"#, + values.iter().map(|v| v.to_string()).join(",") + ); + + let mut file = File::create("proof.cairo").unwrap(); + file.write_all(ser_proof.as_bytes()).unwrap(); + } }