diff --git a/stwo_cairo_verifier/.tool-versions b/stwo_cairo_verifier/.tool-versions index 9558a0ae..5e0dcf64 100644 --- a/stwo_cairo_verifier/.tool-versions +++ b/stwo_cairo_verifier/.tool-versions @@ -1,2 +1,2 @@ -scarb nightly-2024-11-19 +scarb nightly-2024-12-14 starknet-foundry 0.33.0 diff --git a/stwo_cairo_verifier/README.md b/stwo_cairo_verifier/README.md index dc1f1e38..66c8d83b 100644 --- a/stwo_cairo_verifier/README.md +++ b/stwo_cairo_verifier/README.md @@ -26,9 +26,9 @@ Modify [`Scarb.toml`](./Scarb.toml) to use [Starknet Foundary](https://github.co ```diff [dev-dependencies] -- cairo_test = "2.8.5" +- cairo_test = "2.9.2" + snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.33.0" } -+ assert_macros = "2.8.5" ++ assert_macros = "2.9.2" + + [scripts] + test = "snforge test --max-n-steps 100000000" diff --git a/stwo_cairo_verifier/Scarb.toml b/stwo_cairo_verifier/Scarb.toml index 440c2fac..45bd8b2c 100644 --- a/stwo_cairo_verifier/Scarb.toml +++ b/stwo_cairo_verifier/Scarb.toml @@ -16,4 +16,4 @@ sort-module-level-items = true bounded_int = { path = "bounded_int" } [dev-dependencies] -cairo_test = "2.8.5" +cairo_test = "2.9.2" diff --git a/stwo_cairo_verifier/crates/constraint_framework/src/lib.cairo b/stwo_cairo_verifier/crates/constraint_framework/src/lib.cairo new file mode 100644 index 00000000..061713a7 --- /dev/null +++ b/stwo_cairo_verifier/crates/constraint_framework/src/lib.cairo @@ -0,0 +1,134 @@ +use core::dict::{Felt252Dict, Felt252DictTrait}; +use core::nullable::{Nullable, NullableTrait}; +use stwo_verifier_core::channel::{Channel, ChannelImpl}; +use stwo_verifier_core::fields::m31::M31; +use stwo_verifier_core::fields::qm31::{QM31, QM31Impl, QM31One, QM31Zero}; + +/// Represents the value of the prefix sum column at some index. +/// Should be used to eliminate padded rows for the logup sum. +// Copied from: +pub type ClaimedPrefixSum = (QM31, usize); + +#[derive(Drop, Clone)] +pub struct LookupElements { + pub z: QM31, + pub alpha: QM31, + pub alpha_powers: Array, +} + +#[generate_trait] +pub impl LookupElementsImpl of LookupElementsTrait { + fn draw(ref channel: Channel) -> LookupElements { + assert!(N != 0); + let [z, alpha]: [QM31; 2] = (*channel.draw_felts(2).span().try_into().unwrap()).unbox(); + + let mut acc = QM31One::one(); + let mut alpha_powers = array![acc]; + + for _ in 1..N { + acc *= alpha; + alpha_powers.append(acc); + }; + + LookupElements { z, alpha, alpha_powers } + } + + fn combine>( + self: @LookupElements, values: [M31; N], + ) -> QM31 { + let mut alpha_powers = self.alpha_powers.span(); + let mut values_span = IntoSpan::span(@values); + let mut sum = -*self.z; + + while let (Option::Some(alpha), Option::Some(value)) = + (alpha_powers.pop_front(), values_span.pop_front()) { + sum += (*alpha).mul_m31(*value); + }; + + sum + } +} + +#[derive(Destruct)] +pub struct PreprocessedMaskValues { + pub values: Felt252Dict>, +} + +#[generate_trait] +pub impl PreprocessedMaskValuesImpl of PreprocessedMaskValuesTrait { + fn new( + mut preprocessed_mask_values: Span>, + preprocessed_columns: Span, + ) -> PreprocessedMaskValues { + let mut values = Default::default(); + + for preprocessed_column in preprocessed_columns { + let column_mask_values = preprocessed_mask_values.pop_front().unwrap().span(); + let [mask_value]: [QM31; 1] = (*column_mask_values.try_into().unwrap()).unbox(); + values + .insert( + PreprocessedColumnKey::encode(preprocessed_column), + NullableTrait::new(mask_value), + ); + }; + + assert!(preprocessed_mask_values.is_empty()); + + PreprocessedMaskValues { values } + } + + fn get(ref self: PreprocessedMaskValues, preprocessed_column: PreprocessedColumn) -> QM31 { + self.values.get(PreprocessedColumnKey::encode(@preprocessed_column)).deref() + } +} + +#[derive(Debug, Default, Drop)] +enum PreprocessedColumnsAllocationMode { + #[default] + Dynamic, + Static, +} + +#[derive(Drop, Debug, Copy)] +pub enum PreprocessedColumn { + /// Symbolic representation of xor lookup table of the form: `(elem_bits, expand_bits, term)`. + /// Where term is `{ 0 = left operand, 1 = right operand, 2 = xor result }`. + XorTable: (u32, u32, usize), + /// Stores the log size of the column. + IsFirst: u32, + Plonk: usize, +} + +/// An encoding of a [`PreprocessedColumn`] to index into [`Felt252Dict`]. +#[generate_trait] +pub impl PreprocessedColumnKey of PreprocessedColumnKeyTrait { + fn encode(key: @PreprocessedColumn) -> felt252 { + const FELT252_2_POW_32: felt252 = 0x100000000; + // TODO: Is there something like Rust's `core::mem::discriminant` in Cairo? + const XOR_TABLE_DISCRIMINANT: felt252 = 0; + const IS_FIRST_TABLE_DISCRIMINANT: felt252 = 1; + const PLONK_TABLE_DISCRIMINANT: felt252 = 2; + + match key { + PreprocessedColumn::XorTable(( + elem_bits, expand_bits, term, + )) => { + let mut res = (*term).into(); + res = res * FELT252_2_POW_32 + (*expand_bits).into(); + res = res * FELT252_2_POW_32 + (*elem_bits).into(); + res = res * FELT252_2_POW_32 + XOR_TABLE_DISCRIMINANT; + res + }, + PreprocessedColumn::IsFirst(log_size) => { + let mut res = (*log_size).into(); + res = res * FELT252_2_POW_32 + IS_FIRST_TABLE_DISCRIMINANT; + res + }, + PreprocessedColumn::Plonk(v) => { + let mut res = (*v).into(); + res = res * FELT252_2_POW_32 + PLONK_TABLE_DISCRIMINANT; + res + }, + } + } +} diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo index 8bd93f7e..ed4ac5f7 100644 --- a/stwo_cairo_verifier/src/fri.cairo +++ b/stwo_cairo_verifier/src/fri.cairo @@ -54,10 +54,9 @@ pub impl FriVerifierImpl of FriVerifierTrait { fn commit( ref channel: Channel, config: FriConfig, proof: FriProof, column_log_bounds: Span, ) -> Result { - let FriProof { first_layer: first_layer_proof, - inner_layers: mut inner_layer_proofs, - last_layer_poly } = - proof; + let FriProof { + first_layer: first_layer_proof, inner_layers: mut inner_layer_proofs, last_layer_poly, + } = proof; channel.mix_digest(first_layer_proof.commitment); diff --git a/stwo_cairo_verifier/src/pcs/quotients.cairo b/stwo_cairo_verifier/src/pcs/quotients.cairo index 046a2815..45ae0515 100644 --- a/stwo_cairo_verifier/src/pcs/quotients.cairo +++ b/stwo_cairo_verifier/src/pcs/quotients.cairo @@ -297,8 +297,9 @@ fn accumulate_row_quotients( for sample_i in 0..batch_size { let (column_index, _) = sample_batch_columns_and_values[sample_i]; let query_eval_at_column = *queried_values_at_row.at(*column_index); - let ComplexConjugateLineCoeffs { alpha_mul_a, alpha_mul_b, alpha_mul_c } = - *line_coeffs[sample_i]; + let ComplexConjugateLineCoeffs { + alpha_mul_a, alpha_mul_b, alpha_mul_c, + } = *line_coeffs[sample_i]; // The numerator is a line equation passing through // (sample_point.y, sample_value), (conj(sample_point), conj(sample_value)) // evaluated at (domain_point.y, value). diff --git a/stwo_cairo_verifier/src/pcs/verifier.cairo b/stwo_cairo_verifier/src/pcs/verifier.cairo index f866d9d4..8854ee41 100644 --- a/stwo_cairo_verifier/src/pcs/verifier.cairo +++ b/stwo_cairo_verifier/src/pcs/verifier.cairo @@ -74,13 +74,14 @@ pub impl CommitmentSchemeVerifierImpl of CommitmentSchemeVerifierTrait { proof: CommitmentSchemeProof, ref channel: Channel, ) -> Result<(), VerificationError> { - let CommitmentSchemeProof { commitments: _, - sampled_values, - decommitments, - queried_values, - proof_of_work_nonce, - fri_proof } = - proof; + let CommitmentSchemeProof { + commitments: _, + sampled_values, + decommitments, + queried_values, + proof_of_work_nonce, + fri_proof, + } = proof; let mut flattened_sampled_values = array![];