diff --git a/Cargo.toml b/Cargo.toml index 31945b0..ff87dd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ repository = "https://github.com/iosis-tech/cairovm-verifier" version = "0.0.1" [workspace.dependencies] +bail-out = "0.2.0" serde = { version = "1.0", features = ["derive"] } serde_with = "2.3.2" sha3 = "0.10.8" diff --git a/crates/air/Cargo.toml b/crates/air/Cargo.toml index a60fe95..5b7bd98 100644 --- a/crates/air/Cargo.toml +++ b/crates/air/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true version.workspace = true [dependencies] +bail-out.workspace = true serde.workspace = true serde_with.workspace = true starknet-crypto.workspace = true diff --git a/crates/air/src/fixtures/commitment.rs b/crates/air/src/fixtures/commitment.rs index fcd1235..30a3d6f 100644 --- a/crates/air/src/fixtures/commitment.rs +++ b/crates/air/src/fixtures/commitment.rs @@ -1,9 +1,9 @@ use super::{config, interaction_elements, unsent_commitment}; -use crate::trace::Commitment; +use crate::{layout::recursive::global_values::InteractionElements, trace::Commitment}; use cairovm_verifier_commitment::{table, vector}; use starknet_crypto::Felt; -pub fn get() -> Commitment { +pub fn get() -> Commitment { let unsent_commitment = unsent_commitment::get(); let traces_config = config::get(); diff --git a/crates/air/src/layout/mod.rs b/crates/air/src/layout/mod.rs index 8da1124..095e240 100644 --- a/crates/air/src/layout/mod.rs +++ b/crates/air/src/layout/mod.rs @@ -1 +1,74 @@ +use crate::{domains::StarkDomains, public_memory::PublicInput}; +use cairovm_verifier_transcript::transcript::Transcript; +use starknet_crypto::Felt; + pub mod recursive; + +pub trait LayoutTrait { + type InteractionElements; + fn eval_composition_polynomial( + interaction_elements: &Self::InteractionElements, + public_input: &PublicInput, + mask_values: &[Felt], + constraint_coefficients: &[Felt], + point: &Felt, + trace_domain_size: &Felt, + trace_generator: &Felt, + ) -> Result; + + fn eval_oods_polynomial( + column_values: &[Felt], + oods_values: &[Felt], + constraint_coefficients: &[Felt], + point: &Felt, + oods_point: &Felt, + trace_generator: &Felt, + ) -> Felt; + + fn validate( + public_input: &PublicInput, + stark_domains: &StarkDomains, + ) -> Result<(), PublicInputValidateError>; + + fn traces_commit( + transcript: &mut Transcript, + unsent_commitment: &crate::trace::UnsentCommitment, + config: crate::trace::config::Config, + ) -> crate::trace::Commitment; + + fn traces_decommit( + queries: &[Felt], + commitment: crate::trace::Commitment, + decommitment: crate::trace::Decommitment, + witness: crate::trace::Witness, + ) -> Result<(), crate::trace::decommit::Error>; +} + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CompositionPolyEvalError { + #[error("segment not present {segment}")] + SegmentMissing { segment: usize }, +} + +#[derive(Error, Debug)] +pub enum PublicInputValidateError { + #[error("max steps exceeded")] + MaxSteps, + + #[error("trace length invalid")] + TraceLengthInvalid, + + #[error("segment not present {segment}")] + SegmentMissing { segment: usize }, + + #[error("layout code invalid")] + LayoutCodeInvalid, + + #[error("range_check invalid")] + RangeCheckInvalid, + + #[error("invalid number of builtin uses")] + UsesInvalid, +} diff --git a/crates/air/src/layout/recursive/autogenerated.rs b/crates/air/src/layout/recursive/autogenerated.rs index f4af330..cb34e51 100644 --- a/crates/air/src/layout/recursive/autogenerated.rs +++ b/crates/air/src/layout/recursive/autogenerated.rs @@ -6,9 +6,9 @@ use starknet_crypto::Felt; pub fn eval_composition_polynomial_inner( mask_values: &[Felt], constraint_coefficients: &[Felt], - point: Felt, - trace_generator: Felt, - global_values: GlobalValues, + point: &Felt, + trace_generator: &Felt, + global_values: &GlobalValues, ) -> Felt { // Compute powers. let pow0 = point.pow_felt( @@ -866,9 +866,9 @@ pub fn eval_oods_polynomial_inner( column_values: &[Felt], oods_values: &[Felt], constraint_coefficients: &[Felt], - point: Felt, - oods_point: Felt, - trace_generator: Felt, + point: &Felt, + oods_point: &Felt, + trace_generator: &Felt, ) -> Felt { // Compute powers. let pow0 = trace_generator.pow(0_u128); diff --git a/crates/air/src/layout/recursive/global_values.rs b/crates/air/src/layout/recursive/global_values.rs index 2d0e052..704ff71 100644 --- a/crates/air/src/layout/recursive/global_values.rs +++ b/crates/air/src/layout/recursive/global_values.rs @@ -1,3 +1,4 @@ +use cairovm_verifier_transcript::transcript::Transcript; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use starknet_core::serde::unsigned_field_element::UfeHex; @@ -92,3 +93,16 @@ pub struct InteractionElements { #[serde_as(as = "UfeHex")] pub diluted_check_interaction_alpha: Felt, } + +impl InteractionElements { + pub fn new(transcript: &mut Transcript) -> Self { + Self { + memory_multi_column_perm_perm_interaction_elm: transcript.random_felt_to_prover(), + memory_multi_column_perm_hash_interaction_elm0: transcript.random_felt_to_prover(), + range_check16_perm_interaction_elm: transcript.random_felt_to_prover(), + diluted_check_permutation_interaction_elm: transcript.random_felt_to_prover(), + diluted_check_interaction_z: transcript.random_felt_to_prover(), + diluted_check_interaction_alpha: transcript.random_felt_to_prover(), + } + } +} diff --git a/crates/air/src/layout/recursive/mod.rs b/crates/air/src/layout/recursive/mod.rs index a5c4ea1..31aa22d 100644 --- a/crates/air/src/layout/recursive/mod.rs +++ b/crates/air/src/layout/recursive/mod.rs @@ -1,19 +1,20 @@ pub mod autogenerated; pub mod global_values; -pub mod trace; - -use autogenerated::{eval_composition_polynomial_inner, eval_oods_polynomial_inner}; -use global_values::{EcPoint, GlobalValues, InteractionElements}; -use starknet_core::types::NonZeroFelt; -use starknet_crypto::Felt; use crate::{ diluted::get_diluted_product, periodic_columns::{eval_pedersen_x, eval_pedersen_y}, - public_memory::PublicInput, + public_memory::{PublicInput, MAX_LOG_N_STEPS, MAX_RANGE_CHECK}, }; +use bail_out::ensure; +use cairovm_verifier_commitment::table::{commit::table_commit, decommit::table_decommit}; +use global_values::{EcPoint, GlobalValues, InteractionElements}; +use starknet_core::types::NonZeroFelt; +use starknet_crypto::Felt; -// Recursive layout +use super::{CompositionPolyEvalError, LayoutTrait, PublicInputValidateError}; + +// Recursive layout consts pub const BITWISE_RATIO: u32 = 8; pub const BITWISE_ROW_RATIO: u32 = 128; pub const BITWISE_TOTAL_N_BITS: u32 = 251; @@ -73,109 +74,263 @@ pub const SHIFT_POINT_X: Felt = pub const SHIFT_POINT_Y: Felt = Felt::from_hex_unchecked("0x3ca0cfe4b3bc6ddf346d49d06ea0ed34e621062c0e056c1d0405d266e10268a"); -pub fn get_builtins() -> Vec { - vec![builtins::OUTPUT, builtins::PEDERSEN, builtins::RANGE_CHECK, builtins::BITWISE] -} +pub const BUILTINS: [Felt; 4] = + [builtins::OUTPUT, builtins::PEDERSEN, builtins::RANGE_CHECK, builtins::BITWISE]; + +pub struct RecursiveLayout {} + +impl LayoutTrait for RecursiveLayout { + type InteractionElements = InteractionElements; + fn eval_composition_polynomial( + interaction_elements: &Self::InteractionElements, + public_input: &PublicInput, + mask_values: &[Felt], + constraint_coefficients: &[Felt], + point: &Felt, + trace_domain_size: &Felt, + trace_generator: &Felt, + ) -> Result { + let memory_z = interaction_elements.memory_multi_column_perm_perm_interaction_elm; + let memory_alpha = interaction_elements.memory_multi_column_perm_hash_interaction_elm0; + + // Public memory + let public_memory_column_size = trace_domain_size + .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(PUBLIC_MEMORY_STEP))); + assert!(public_memory_column_size < u128::MAX.into()); + let public_memory_prod_ratio = public_input.get_public_memory_product_ratio( + memory_z, + memory_alpha, + public_memory_column_size, + ); + + // Diluted + let diluted_z = interaction_elements.diluted_check_interaction_z; + let diluted_alpha = interaction_elements.diluted_check_interaction_alpha; + let diluted_prod = get_diluted_product( + DILUTED_N_BITS.into(), + DILUTED_SPACING.into(), + diluted_z, + diluted_alpha, + ); + + // Periodic columns + let n_steps = Felt::TWO.pow_felt(&public_input.log_n_steps); + let n_pedersen_hash_copies = n_steps.field_div(&NonZeroFelt::from_felt_unchecked( + Felt::from(PEDERSEN_BUILTIN_RATIO * PEDERSEN_BUILTIN_REPETITIONS), + )); + assert!(n_pedersen_hash_copies < u128::MAX.into()); + let pedersen_point = point.pow_felt(&n_pedersen_hash_copies); + let pedersen_points_x = eval_pedersen_x(pedersen_point); + let pedersen_points_y = eval_pedersen_y(pedersen_point); -pub fn eval_composition_polynomial( - interaction_elements: InteractionElements, - public_input: &PublicInput, - mask_values: &[Felt], - constraint_coefficients: &[Felt], - point: Felt, - trace_domain_size: Felt, - trace_generator: Felt, -) -> Felt { - let memory_z = interaction_elements.memory_multi_column_perm_perm_interaction_elm; - let memory_alpha = interaction_elements.memory_multi_column_perm_hash_interaction_elm0; - - // Public memory - let public_memory_column_size = trace_domain_size - .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(PUBLIC_MEMORY_STEP))); - assert!(public_memory_column_size < u128::MAX.into()); - let public_memory_prod_ratio = public_input.get_public_memory_product_ratio( - memory_z, - memory_alpha, - public_memory_column_size, - ); - - // Diluted - let diluted_z = interaction_elements.diluted_check_interaction_z; - let diluted_alpha = interaction_elements.diluted_check_interaction_alpha; - let diluted_prod = get_diluted_product( - DILUTED_N_BITS.into(), - DILUTED_SPACING.into(), - diluted_z, - diluted_alpha, - ); - - // Periodic columns - let n_steps = Felt::TWO.pow_felt(&public_input.log_n_steps); - let n_pedersen_hash_copies = n_steps.field_div(&NonZeroFelt::from_felt_unchecked(Felt::from( - PEDERSEN_BUILTIN_RATIO * PEDERSEN_BUILTIN_REPETITIONS, - ))); - assert!(n_pedersen_hash_copies < u128::MAX.into()); - let pedersen_point = point.pow_felt(&n_pedersen_hash_copies); - let pedersen_points_x = eval_pedersen_x(pedersen_point); - let pedersen_points_y = eval_pedersen_y(pedersen_point); - - let global_values = GlobalValues { - trace_length: trace_domain_size, - initial_pc: public_input.segments.get(segments::PROGRAM).unwrap().begin_addr, - final_pc: public_input.segments.get(segments::PROGRAM).unwrap().stop_ptr, - initial_ap: public_input.segments.get(segments::EXECUTION).unwrap().begin_addr, - final_ap: public_input.segments.get(segments::EXECUTION).unwrap().stop_ptr, - initial_pedersen_addr: public_input.segments.get(segments::PEDERSEN).unwrap().begin_addr, - initial_range_check_addr: public_input + let global_values = GlobalValues { + trace_length: *trace_domain_size, + initial_pc: public_input + .segments + .get(segments::PROGRAM) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::PROGRAM })? + .begin_addr, + final_pc: public_input + .segments + .get(segments::PROGRAM) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::PROGRAM })? + .stop_ptr, + initial_ap: public_input + .segments + .get(segments::EXECUTION) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::EXECUTION })? + .begin_addr, + final_ap: public_input + .segments + .get(segments::EXECUTION) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::EXECUTION })? + .stop_ptr, + initial_pedersen_addr: public_input + .segments + .get(segments::PEDERSEN) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::PEDERSEN })? + .begin_addr, + initial_range_check_addr: public_input + .segments + .get(segments::RANGE_CHECK) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::RANGE_CHECK })? + .begin_addr, + initial_bitwise_addr: public_input + .segments + .get(segments::BITWISE) + .ok_or(CompositionPolyEvalError::SegmentMissing { segment: segments::BITWISE })? + .begin_addr, + range_check_min: public_input.range_check_min, + range_check_max: public_input.range_check_max, + offset_size: Felt::from(0x10000), // 2**16 + half_offset_size: Felt::from(0x8000), // 2**15 + pedersen_shift_point: EcPoint { x: SHIFT_POINT_X, y: SHIFT_POINT_Y }, + pedersen_points_x, + pedersen_points_y, + memory_multi_column_perm_perm_interaction_elm: memory_z, + memory_multi_column_perm_hash_interaction_elm0: memory_alpha, + range_check16_perm_interaction_elm: interaction_elements + .range_check16_perm_interaction_elm, + diluted_check_permutation_interaction_elm: interaction_elements + .diluted_check_permutation_interaction_elm, + diluted_check_interaction_z: diluted_z, + diluted_check_interaction_alpha: diluted_alpha, + memory_multi_column_perm_perm_public_memory_prod: public_memory_prod_ratio, + range_check16_perm_public_memory_prod: Felt::from(1), + diluted_check_first_elm: Felt::from(0), + diluted_check_permutation_public_memory_prod: Felt::from(1), + diluted_check_final_cum_val: diluted_prod, + }; + + Ok(autogenerated::eval_composition_polynomial_inner( + mask_values, + constraint_coefficients, + point, + trace_generator, + &global_values, + )) + } + fn eval_oods_polynomial( + column_values: &[Felt], + oods_values: &[Felt], + constraint_coefficients: &[Felt], + point: &Felt, + oods_point: &Felt, + trace_generator: &Felt, + ) -> Felt { + autogenerated::eval_oods_polynomial_inner( + column_values, + oods_values, + constraint_coefficients, + point, + oods_point, + trace_generator, + ) + } + fn traces_commit( + transcript: &mut cairovm_verifier_transcript::transcript::Transcript, + unsent_commitment: &crate::trace::UnsentCommitment, + config: crate::trace::config::Config, + ) -> crate::trace::Commitment { + // Read original commitment. + let original_commitment = + table_commit(transcript, unsent_commitment.original, config.original); + + // Generate interaction elements for the first interaction. + let interaction_elements = Self::InteractionElements::new(transcript); + + // Read interaction commitment. + let interaction_commitment = + table_commit(transcript, unsent_commitment.interaction, config.interaction); + + crate::trace::Commitment { + original: original_commitment, + interaction_elements, + interaction: interaction_commitment, + } + } + fn traces_decommit( + queries: &[Felt], + commitment: crate::trace::Commitment, + decommitment: crate::trace::Decommitment, + witness: crate::trace::Witness, + ) -> Result<(), crate::trace::decommit::Error> { + Ok(table_decommit(commitment.original, queries, decommitment.original, witness.original) + .and(table_decommit( + commitment.interaction, + queries, + decommitment.interaction, + witness.interaction, + ))?) + } + fn validate( + public_input: &PublicInput, + stark_domains: &crate::domains::StarkDomains, + ) -> Result<(), PublicInputValidateError> { + ensure!(public_input.log_n_steps < MAX_LOG_N_STEPS, PublicInputValidateError::MaxSteps); + + let n_steps = Felt::TWO.pow_felt(&public_input.log_n_steps); + let trace_length = stark_domains.trace_domain_size; + ensure!( + n_steps * Felt::from(CPU_COMPONENT_HEIGHT) * Felt::from(CPU_COMPONENT_STEP) + == trace_length, + PublicInputValidateError::TraceLengthInvalid + ); + + ensure!( + Felt::ZERO <= public_input.range_check_min, + PublicInputValidateError::RangeCheckInvalid + ); + ensure!( + public_input.range_check_min < public_input.range_check_max, + PublicInputValidateError::RangeCheckInvalid + ); + ensure!( + public_input.range_check_max <= MAX_RANGE_CHECK, + PublicInputValidateError::RangeCheckInvalid + ); + + ensure!( + public_input.layout == LAYOUT_CODE.into(), + PublicInputValidateError::LayoutCodeInvalid + ); + + let output_uses = public_input + .segments + .get(segments::OUTPUT) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .stop_ptr + - public_input + .segments + .get(segments::OUTPUT) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .begin_addr; + ensure!(output_uses < u128::MAX.into(), PublicInputValidateError::UsesInvalid); + + let pedersen_copies = trace_length + .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(PEDERSEN_BUILTIN_ROW_RATIO))); + let pedersen_uses = (public_input + .segments + .get(segments::PEDERSEN) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .stop_ptr + - public_input + .segments + .get(segments::PEDERSEN) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .begin_addr) + .field_div(&NonZeroFelt::from_felt_unchecked(Felt::THREE)); + ensure!(pedersen_uses < pedersen_copies, PublicInputValidateError::UsesInvalid); + + let range_check_copies = trace_length.field_div(&NonZeroFelt::from_felt_unchecked( + Felt::from(RANGE_CHECK_BUILTIN_ROW_RATIO), + )); + let range_check_uses = public_input .segments .get(segments::RANGE_CHECK) - .unwrap() - .begin_addr, - initial_bitwise_addr: public_input.segments.get(segments::BITWISE).unwrap().begin_addr, - range_check_min: public_input.range_check_min, - range_check_max: public_input.range_check_max, - offset_size: Felt::from(0x10000), // 2**16 - half_offset_size: Felt::from(0x8000), // 2**15 - pedersen_shift_point: EcPoint { x: SHIFT_POINT_X, y: SHIFT_POINT_Y }, - pedersen_points_x, - pedersen_points_y, - memory_multi_column_perm_perm_interaction_elm: memory_z, - memory_multi_column_perm_hash_interaction_elm0: memory_alpha, - range_check16_perm_interaction_elm: interaction_elements.range_check16_perm_interaction_elm, - diluted_check_permutation_interaction_elm: interaction_elements - .diluted_check_permutation_interaction_elm, - diluted_check_interaction_z: diluted_z, - diluted_check_interaction_alpha: diluted_alpha, - memory_multi_column_perm_perm_public_memory_prod: public_memory_prod_ratio, - range_check16_perm_public_memory_prod: Felt::from(1), - diluted_check_first_elm: Felt::from(0), - diluted_check_permutation_public_memory_prod: Felt::from(1), - diluted_check_final_cum_val: diluted_prod, - }; - - eval_composition_polynomial_inner( - mask_values, - constraint_coefficients, - point, - trace_generator, - global_values, - ) -} + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .stop_ptr + - public_input + .segments + .get(segments::RANGE_CHECK) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .begin_addr; + ensure!(range_check_uses < range_check_copies, PublicInputValidateError::UsesInvalid); -pub fn eval_oods_polynomial( - column_values: &[Felt], - oods_values: &[Felt], - constraint_coefficients: &[Felt], - point: Felt, - oods_point: Felt, - trace_generator: Felt, -) -> Felt { - eval_oods_polynomial_inner( - column_values, - oods_values, - constraint_coefficients, - point, - oods_point, - trace_generator, - ) + let bitwise_copies = trace_length + .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(BITWISE_ROW_RATIO))); + let bitwise_uses = (public_input + .segments + .get(segments::BITWISE) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .stop_ptr + - public_input + .segments + .get(segments::BITWISE) + .ok_or(PublicInputValidateError::SegmentMissing { segment: segments::OUTPUT })? + .begin_addr) + .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(0x5))); + ensure!(bitwise_uses < bitwise_copies, PublicInputValidateError::UsesInvalid); + Ok(()) + } } diff --git a/crates/air/src/layout/recursive/trace.rs b/crates/air/src/layout/recursive/trace.rs deleted file mode 100644 index 8aae5b8..0000000 --- a/crates/air/src/layout/recursive/trace.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Reads the traces commitment from the transcript. -// Returns the commitment, along with GlobalValue required to evaluate the constraint polynomial. - -use cairovm_verifier_commitment::table::{commit::table_commit, decommit::table_decommit}; -use cairovm_verifier_transcript::transcript::Transcript; -use starknet_crypto::Felt; - -use crate::trace::{ - config::Config, decommit::Error, Commitment, Decommitment, UnsentCommitment, Witness, -}; - -use super::global_values::InteractionElements; - -pub fn traces_commit( - transcript: &mut Transcript, - unsent_commitment: &UnsentCommitment, - config: Config, -) -> Commitment { - // Read original commitment. - let original_commitment = table_commit(transcript, unsent_commitment.original, config.original); - - // Generate interaction elements for the first interaction. - let interaction_elements = InteractionElements { - memory_multi_column_perm_perm_interaction_elm: transcript.random_felt_to_prover(), - memory_multi_column_perm_hash_interaction_elm0: transcript.random_felt_to_prover(), - range_check16_perm_interaction_elm: transcript.random_felt_to_prover(), - diluted_check_permutation_interaction_elm: transcript.random_felt_to_prover(), - diluted_check_interaction_z: transcript.random_felt_to_prover(), - diluted_check_interaction_alpha: transcript.random_felt_to_prover(), - }; - - // Read interaction commitment. - let interaction_commitment = - table_commit(transcript, unsent_commitment.interaction, config.interaction); - - Commitment { - original: original_commitment, - interaction_elements, - interaction: interaction_commitment, - } -} - -// Verifies a decommitment for the traces at the query indices. -// decommitment - holds the commited values of the leaves at the query_indices. -pub fn traces_decommit( - queries: &[Felt], - commitment: Commitment, - decommitment: Decommitment, - witness: Witness, -) -> Result<(), Error> { - Ok(table_decommit(commitment.original, queries, decommitment.original, witness.original).and( - table_decommit( - commitment.interaction, - queries, - decommitment.interaction, - witness.interaction, - ), - )?) -} diff --git a/crates/air/src/public_memory.rs b/crates/air/src/public_memory.rs index d41e2f5..205debe 100644 --- a/crates/air/src/public_memory.rs +++ b/crates/air/src/public_memory.rs @@ -1,18 +1,11 @@ -use crate::{ - domains::StarkDomains, - layout::recursive::{ - segments, BITWISE_ROW_RATIO, CPU_COMPONENT_HEIGHT, CPU_COMPONENT_STEP, LAYOUT_CODE, - PEDERSEN_BUILTIN_ROW_RATIO, RANGE_CHECK_BUILTIN_ROW_RATIO, - }, - types::{ContinuousPageHeader, Page, SegmentInfo}, -}; +use crate::types::{ContinuousPageHeader, Page, SegmentInfo}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use starknet_core::{serde::unsigned_field_element::UfeHex, types::NonZeroFelt}; use starknet_crypto::{pedersen_hash, poseidon_hash_many, Felt}; -const MAX_LOG_N_STEPS: Felt = Felt::from_hex_unchecked("50"); -const MAX_RANGE_CHECK: Felt = Felt::from_hex_unchecked("0xffff"); // 2 ** 16 - 1 +pub const MAX_LOG_N_STEPS: Felt = Felt::from_hex_unchecked("50"); +pub const MAX_RANGE_CHECK: Felt = Felt::from_hex_unchecked("0xffff"); // 2 ** 16 - 1 #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -103,47 +96,6 @@ impl PublicInput { poseidon_hash_many(&hash_data) } - - pub fn validate(&self, stark_domains: &StarkDomains) { - assert!(self.log_n_steps < MAX_LOG_N_STEPS); - let n_steps = Felt::TWO.pow_felt(&self.log_n_steps); - let trace_length = stark_domains.trace_domain_size; - assert!( - n_steps * Felt::from(CPU_COMPONENT_HEIGHT) * Felt::from(CPU_COMPONENT_STEP) - == trace_length - ); - - assert!(Felt::ZERO <= self.range_check_min); - assert!(self.range_check_min < self.range_check_max); - assert!(self.range_check_max <= MAX_RANGE_CHECK); - - assert!(self.layout == LAYOUT_CODE.into()); - - let output_uses = self.segments.get(segments::OUTPUT).unwrap().stop_ptr - - self.segments.get(segments::OUTPUT).unwrap().begin_addr; - assert!(output_uses < u128::MAX.into()); - - let pedersen_copies = trace_length - .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(PEDERSEN_BUILTIN_ROW_RATIO))); - let pedersen_uses = (self.segments.get(segments::PEDERSEN).unwrap().stop_ptr - - self.segments.get(segments::PEDERSEN).unwrap().begin_addr) - .field_div(&NonZeroFelt::from_felt_unchecked(Felt::THREE)); - assert!(pedersen_uses < pedersen_copies); - - let range_check_copies = trace_length.field_div(&NonZeroFelt::from_felt_unchecked( - Felt::from(RANGE_CHECK_BUILTIN_ROW_RATIO), - )); - let range_check_uses = self.segments.get(segments::RANGE_CHECK).unwrap().stop_ptr - - self.segments.get(segments::RANGE_CHECK).unwrap().begin_addr; - assert!(range_check_uses < range_check_copies); - - let bitwise_copies = trace_length - .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(BITWISE_ROW_RATIO))); - let bitwise_uses = (self.segments.get(segments::BITWISE).unwrap().stop_ptr - - self.segments.get(segments::BITWISE).unwrap().begin_addr) - .field_div(&NonZeroFelt::from_felt_unchecked(Felt::from(0x5))); - assert!(bitwise_uses < bitwise_copies); - } } fn get_continuous_pages_product(page_headers: &[ContinuousPageHeader]) -> (Felt, Felt) { diff --git a/crates/air/src/trace/mod.rs b/crates/air/src/trace/mod.rs index 3eb1b61..dabd9e8 100644 --- a/crates/air/src/trace/mod.rs +++ b/crates/air/src/trace/mod.rs @@ -1,7 +1,6 @@ pub mod config; pub mod decommit; -use crate::layout::recursive::global_values::InteractionElements; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use starknet_core::serde::unsigned_field_element::UfeHex; @@ -20,7 +19,7 @@ pub struct UnsentCommitment { // Commitment for the Traces component. #[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct Commitment { +pub struct Commitment { // Commitment to the first trace. pub original: cairovm_verifier_commitment::table::types::Commitment, // The interaction elements that were sent to the prover after the first trace commitment (e.g. diff --git a/crates/stark/Cargo.toml b/crates/stark/Cargo.toml index ac6fc5a..84f9b9d 100644 --- a/crates/stark/Cargo.toml +++ b/crates/stark/Cargo.toml @@ -14,6 +14,7 @@ serde_with.workspace = true starknet-crypto.workspace = true starknet-core.workspace = true thiserror.workspace = true +bail-out.workspace = true cairovm_verifier_air.workspace = true cairovm_verifier_pow.workspace = true diff --git a/crates/stark/src/commit.rs b/crates/stark/src/commit.rs index 1850a68..0d5e1a3 100644 --- a/crates/stark/src/commit.rs +++ b/crates/stark/src/commit.rs @@ -1,6 +1,9 @@ use cairovm_verifier_air::{ domains::StarkDomains, - layout::recursive::{trace::traces_commit, CONSTRAINT_DEGREE, MASK_SIZE, N_CONSTRAINTS}, + layout::{ + recursive::{CONSTRAINT_DEGREE, MASK_SIZE, N_CONSTRAINTS}, + LayoutTrait, + }, public_memory::PublicInput, }; use cairovm_verifier_commitment::table::commit::table_commit; @@ -11,21 +14,21 @@ use starknet_crypto::Felt; use crate::{ config::StarkConfig, - oods::verify_oods, + oods::{self, verify_oods}, types::{StarkCommitment, StarkUnsentCommitment}, }; // STARK commitment phase. -pub fn stark_commit( +pub fn stark_commit( transcript: &mut Transcript, public_input: &PublicInput, unsent_commitment: &StarkUnsentCommitment, config: &StarkConfig, stark_domains: &StarkDomains, -) -> Result { +) -> Result, Error> { // Read the commitment of the 'traces' component. let traces_commitment = - traces_commit(transcript, &unsent_commitment.traces, config.traces.to_owned()); + Layout::traces_commit(transcript, &unsent_commitment.traces, config.traces.to_owned()); // Generate interaction values after traces commitment. let composition_alpha = transcript.random_felt_to_prover(); @@ -42,15 +45,15 @@ pub fn stark_commit( transcript.read_felt_vector_from_prover(&unsent_commitment.oods_values); // Check that the trace and the composition agree at oods_point. - verify_oods( + verify_oods::( &unsent_commitment.oods_values, - traces_commitment.interaction_elements.to_owned(), + &traces_commitment.interaction_elements, public_input, &traces_coefficients, - interaction_after_composition, - stark_domains.trace_domain_size, - stark_domains.trace_generator, - ); + &interaction_after_composition, + &stark_domains.trace_domain_size, + &stark_domains.trace_generator, + )?; // Generate interaction values after OODS. let oods_alpha = transcript.random_felt_to_prover(); @@ -92,4 +95,7 @@ use thiserror::Error; pub enum Error { #[error("POW Error")] POW(#[from] pow::Error), + + #[error("OodsVerifyError Error")] + Oods(#[from] oods::OodsVerifyError), } diff --git a/crates/stark/src/fixtures/commitment.rs b/crates/stark/src/fixtures/commitment.rs index f0d0699..13d6c7f 100644 --- a/crates/stark/src/fixtures/commitment.rs +++ b/crates/stark/src/fixtures/commitment.rs @@ -1,3 +1,4 @@ +use cairovm_verifier_air::layout::recursive::global_values::InteractionElements; use cairovm_verifier_commitment::{ table::{config::Config as TableCommitmentConfig, types::Commitment as TableCommitment}, vector::{config::Config as VectorCommitmentConfig, types::Commitment as VectorCommitment}, @@ -8,7 +9,7 @@ use crate::types::StarkCommitment; use super::oods_values; -pub fn get() -> StarkCommitment { +pub fn get() -> StarkCommitment { StarkCommitment { traces: cairovm_verifier_air::fixtures::commitment::get(), composition: TableCommitment { diff --git a/crates/stark/src/oods.rs b/crates/stark/src/oods.rs index 2666a19..e367f56 100644 --- a/crates/stark/src/oods.rs +++ b/crates/stark/src/oods.rs @@ -1,8 +1,6 @@ +use bail_out::assure; use cairovm_verifier_air::{ - layout::recursive::{ - eval_composition_polynomial, eval_oods_polynomial, global_values::InteractionElements, - CONSTRAINT_DEGREE, - }, + layout::{recursive::CONSTRAINT_DEGREE, CompositionPolyEvalError, LayoutTrait}, public_memory::PublicInput, trace, }; @@ -18,16 +16,16 @@ pub struct OodsEvaluationInfo { // Checks that the trace and the compostion agree at oods_point, assuming the prover provided us // with the proper evaluations. -pub fn verify_oods( +pub fn verify_oods( oods: &[Felt], - interaction_elements: InteractionElements, + interaction_elements: &Layout::InteractionElements, public_input: &PublicInput, constraint_coefficients: &[Felt], - oods_point: Felt, - trace_domain_size: Felt, - trace_generator: Felt, -) { - let composition_from_trace = eval_composition_polynomial( + oods_point: &Felt, + trace_domain_size: &Felt, + trace_generator: &Felt, +) -> Result<(), OodsVerifyError> { + let composition_from_trace = Layout::eval_composition_polynomial( interaction_elements, public_input, &oods[0..oods.len() - 2], @@ -35,15 +33,31 @@ pub fn verify_oods( oods_point, trace_domain_size, trace_generator, - ); + )?; // TODO support degree > 2? let claimed_composition = oods[oods.len() - 2] + oods[oods.len() - 1] * oods_point; - assert_eq!(composition_from_trace, claimed_composition); + assure!( + composition_from_trace == claimed_composition, + OodsVerifyError::EvaluationInvalid { + expected: claimed_composition, + actual: composition_from_trace + } + ) +} + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum OodsVerifyError { + #[error("oods invalid {expected} - {actual}")] + EvaluationInvalid { expected: Felt, actual: Felt }, + #[error("CompositionPolyEval Error")] + CompositionPolyEvalError(#[from] CompositionPolyEvalError), } -pub fn eval_oods_boundary_poly_at_points( +pub fn eval_oods_boundary_poly_at_points( n_original_columns: usize, n_interaction_columns: usize, eval_info: OodsEvaluationInfo, @@ -83,13 +97,13 @@ pub fn eval_oods_boundary_poly_at_points( [i * CONSTRAINT_DEGREE as usize..(i + 1) * CONSTRAINT_DEGREE as usize], ); - evaluations.push(eval_oods_polynomial( + evaluations.push(Layout::eval_oods_polynomial( &column_values, &eval_info.oods_values, &eval_info.constraint_coefficients, - point, - eval_info.oods_point, - eval_info.trace_generator, + &point, + &eval_info.oods_point, + &eval_info.trace_generator, )); } diff --git a/crates/stark/src/stark.rs b/crates/stark/src/stark.rs index 779b55b..86d9031 100644 --- a/crates/stark/src/stark.rs +++ b/crates/stark/src/stark.rs @@ -3,13 +3,14 @@ use crate::{ }; impl StarkProof { - pub fn verify(&self, security_bits: Felt) -> Result { + pub fn verify(&self, security_bits: Felt) -> Result { self.config.validate(security_bits)?; // Validate the public input. let stark_domains = StarkDomains::new(self.config.log_trace_domain_size, self.config.log_n_cosets); - self.public_input.validate(&stark_domains); + + Layout::validate(&self.public_input, &stark_domains)?; // Compute the initial hash seed for the Fiat-Shamir transcript. let digest = self.public_input.get_public_input_hash(); @@ -17,7 +18,7 @@ impl StarkProof { let mut transcript = Transcript::new(digest); // STARK commitment phase. - let stark_commitment = stark_commit( + let stark_commitment = stark_commit::( &mut transcript, &self.public_input, &self.unsent_commitment, @@ -33,7 +34,7 @@ impl StarkProof { ); // STARK verify phase. - stark_verify( + stark_verify::( NUM_COLUMNS_FIRST as usize, NUM_COLUMNS_SECOND as usize, &queries, @@ -48,7 +49,10 @@ impl StarkProof { use cairovm_verifier_air::{ domains::StarkDomains, - layout::recursive::{NUM_COLUMNS_FIRST, NUM_COLUMNS_SECOND}, + layout::{ + recursive::{NUM_COLUMNS_FIRST, NUM_COLUMNS_SECOND}, + LayoutTrait, PublicInputValidateError, + }, }; use cairovm_verifier_transcript::transcript::Transcript; use starknet_crypto::Felt; @@ -59,6 +63,9 @@ pub enum Error { #[error("Vector Error")] Validation(#[from] crate::config::Error), + #[error("PublicInputValidateError Error")] + PublicInputValidateError(#[from] PublicInputValidateError), + #[error("Commit Error")] Commit(#[from] crate::commit::Error), diff --git a/crates/stark/src/tests/commit.rs b/crates/stark/src/tests/commit.rs index 13f5f1d..e9377d1 100644 --- a/crates/stark/src/tests/commit.rs +++ b/crates/stark/src/tests/commit.rs @@ -1,4 +1,4 @@ -use cairovm_verifier_air::fixtures::public_input; +use cairovm_verifier_air::{fixtures::public_input, layout::recursive::RecursiveLayout}; use cairovm_verifier_transcript::transcript::Transcript; use starknet_crypto::Felt; @@ -22,8 +22,14 @@ pub fn test_stark_commit() { let stark_domains = domains::get(); assert_eq!( - stark_commit(&mut transcript, &public_input, &unsent_commitment, &config, &stark_domains) - .unwrap(), + stark_commit::( + &mut transcript, + &public_input, + &unsent_commitment, + &config, + &stark_domains + ) + .unwrap(), commitment::get() ); diff --git a/crates/stark/src/tests/proof.rs b/crates/stark/src/tests/proof.rs index 73ff933..0becaaa 100644 --- a/crates/stark/src/tests/proof.rs +++ b/crates/stark/src/tests/proof.rs @@ -2,7 +2,7 @@ use crate::{ fixtures::{config, unsent_commitment, witness}, types::StarkProof, }; -use cairovm_verifier_air::fixtures::public_input; +use cairovm_verifier_air::{fixtures::public_input, layout::recursive::RecursiveLayout}; use starknet_crypto::Felt; #[test] @@ -16,5 +16,5 @@ fn test_stark_proof_fibonacci_verify() { witness: witness::get(), }; - stark_proof.verify(security_bits).unwrap(); + stark_proof.verify::(security_bits).unwrap(); } diff --git a/crates/stark/src/tests/verify.rs b/crates/stark/src/tests/verify.rs index 8d1c935..ed03ec3 100644 --- a/crates/stark/src/tests/verify.rs +++ b/crates/stark/src/tests/verify.rs @@ -2,7 +2,9 @@ use crate::{ fixtures::{commitment, domains, witness}, verify::stark_verify, }; -use cairovm_verifier_air::layout::recursive::{NUM_COLUMNS_FIRST, NUM_COLUMNS_SECOND}; +use cairovm_verifier_air::layout::recursive::{ + RecursiveLayout, NUM_COLUMNS_FIRST, NUM_COLUMNS_SECOND, +}; use cairovm_verifier_fri::fixtures::queries; #[test] @@ -12,7 +14,7 @@ pub fn test_stark_verify() { let witness = witness::get(); let stark_domains = domains::get(); - stark_verify( + stark_verify::( NUM_COLUMNS_FIRST as usize, NUM_COLUMNS_SECOND as usize, &queries, diff --git a/crates/stark/src/types.rs b/crates/stark/src/types.rs index 6a04dad..239c406 100644 --- a/crates/stark/src/types.rs +++ b/crates/stark/src/types.rs @@ -30,8 +30,8 @@ pub struct StarkUnsentCommitment { #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct StarkCommitment { - pub traces: cairovm_verifier_air::trace::Commitment, +pub struct StarkCommitment { + pub traces: cairovm_verifier_air::trace::Commitment, pub composition: cairovm_verifier_commitment::table::types::Commitment, #[serde_as(as = "UfeHex")] pub interaction_after_composition: Felt, diff --git a/crates/stark/src/verify.rs b/crates/stark/src/verify.rs index 664f0b7..91b9747 100644 --- a/crates/stark/src/verify.rs +++ b/crates/stark/src/verify.rs @@ -1,4 +1,4 @@ -use cairovm_verifier_air::{domains::StarkDomains, layout::recursive::trace::traces_decommit}; +use cairovm_verifier_air::{domains::StarkDomains, layout::LayoutTrait}; use cairovm_verifier_commitment::table::decommit::table_decommit; use cairovm_verifier_fri::{ fri::{self, fri_verify}, @@ -13,16 +13,16 @@ use crate::{ }; // STARK verify phase. -pub fn stark_verify( +pub fn stark_verify( n_original_columns: usize, n_interaction_columns: usize, queries: &[Felt], - commitment: StarkCommitment, + commitment: StarkCommitment, witness: &StarkWitness, stark_domains: &StarkDomains, ) -> Result<(), Error> { // First layer decommit. - traces_decommit( + Layout::traces_decommit( queries, commitment.traces, witness.traces_decommitment.to_owned(), @@ -46,7 +46,7 @@ pub fn stark_verify( trace_generator: stark_domains.trace_generator, constraint_coefficients: commitment.interaction_after_oods, }; - let oods_poly_evals = eval_oods_boundary_poly_at_points( + let oods_poly_evals = eval_oods_boundary_poly_at_points::( n_original_columns, n_interaction_columns, eval_info,