diff --git a/src/air/traces.cairo b/src/air/traces.cairo index 57b8a3789..b2f0659a4 100644 --- a/src/air/traces.cairo +++ b/src/air/traces.cairo @@ -1,7 +1,6 @@ use cairo_verifier::channel::channel::ChannelTrait; -use cairo_verifier::table_commitment::{ - TableUnsentCommitment, TableCommitment, TableDecommitment, TableCommitmentWitness, table_commit, - table_decommit, +use cairo_verifier::table_commitment::table_commitment::{ + TableCommitment, TableDecommitment, TableCommitmentWitness, table_commit, table_decommit, }; use cairo_verifier::air::{public_input::PublicInput, traces_config::TracesConfig}; use cairo_verifier::channel::channel::Channel; @@ -18,8 +17,8 @@ use cairo_verifier::channel::channel::Channel; // values from the channel. #[derive(Drop, Copy)] struct TracesUnsentCommitment { - original: TableUnsentCommitment, - interaction: TableUnsentCommitment, + original: felt252, + interaction: felt252, } // Commitment for the Traces component. @@ -62,11 +61,15 @@ fn traces_commit( config: TracesConfig ) -> TracesCommitment { // Read original commitment. - let original_commitment = table_commit(unsent_commitment.original, config.original); + let original_commitment = table_commit( + ref channel, unsent_commitment.original, config.original + ); // Generate interaction elements for the first interaction. let interaction_elements = channel.random_felts_to_prover(n_interaction_elements); // Read interaction commitment. - let interaction_commitment = table_commit(unsent_commitment.interaction, config.interaction); + let interaction_commitment = table_commit( + ref channel, unsent_commitment.interaction, config.interaction + ); TracesCommitment { public_input: public_input, diff --git a/src/air/traces_config.cairo b/src/air/traces_config.cairo index ea5a0a8b4..96d289541 100644 --- a/src/air/traces_config.cairo +++ b/src/air/traces_config.cairo @@ -1,5 +1,7 @@ use cairo_verifier::vector_commitment::vector_commitment::VectorCommitmentConfigTrait; -use cairo_verifier::{common::asserts::assert_in_range, table_commitment::TableCommitmentConfig}; +use cairo_verifier::{ + common::asserts::assert_in_range, table_commitment::table_commitment::TableCommitmentConfig +}; const MAX_N_COLUMNS: felt252 = 128; const AIR_LAYOUT_N_ORIGINAL_COLUMNS: felt252 = 12; diff --git a/src/common/array_append.cairo b/src/common/array_append.cairo index af725bdb2..08f13814e 100644 --- a/src/common/array_append.cairo +++ b/src/common/array_append.cairo @@ -45,6 +45,29 @@ impl ArrayU32AppendFelt of ArrayAppendTrait { } } +impl ArrayU32AppendFeltsSpan of ArrayAppendTrait> { + fn append_little_endian(ref self: Array, element: Span) { + let mut i = 0; + loop { + if i == element.len() { + break; + } + self.append_little_endian(*element[i]); + i += 1; + }; + } + fn append_big_endian(ref self: Array, element: Span) { + let mut i = 0; + loop { + if i == element.len() { + break; + } + self.append_big_endian(*element[i]); + i += 1; + }; + } +} + impl ArrayU32AppendU128 of ArrayAppendTrait { fn append_little_endian(ref self: Array, mut element: u128) { let mut i = 4; diff --git a/src/deserialization/fri.cairo b/src/deserialization/fri.cairo index 47e9ad0b5..825d1bfeb 100644 --- a/src/deserialization/fri.cairo +++ b/src/deserialization/fri.cairo @@ -1,6 +1,6 @@ use cairo_verifier::{ fri::{fri_config::FriConfig, fri::{FriUnsentCommitment, FriWitness, FriLayerWitness}}, - table_commitment::{TableCommitmentConfig, TableCommitmentWitness, TableUnsentCommitment}, + table_commitment::table_commitment::{TableCommitmentConfig, TableCommitmentWitness}, vector_commitment::vector_commitment::{VectorCommitmentConfig, VectorCommitmentWitness}, }; @@ -56,13 +56,13 @@ struct FriUnsentCommitmentWithSerde { } impl IntoFriUnsentCommitment of Into { fn into(self: FriUnsentCommitmentWithSerde) -> FriUnsentCommitment { - let mut inner_layers = ArrayTrait::::new(); + let mut inner_layers = ArrayTrait::::new(); let mut i = 0; loop { if i == self.inner_layers.len() { break; } - inner_layers.append(TableUnsentCommitment { vector: *self.inner_layers[i] }); + inner_layers.append(*self.inner_layers[i]); i += 1; }; FriUnsentCommitment { diff --git a/src/deserialization/traces.cairo b/src/deserialization/traces.cairo index 879abb114..d3bd2a5df 100644 --- a/src/deserialization/traces.cairo +++ b/src/deserialization/traces.cairo @@ -9,8 +9,8 @@ use cairo_verifier::{ VectorCommitmentWitnessWithSerde } }, - table_commitment::{ - TableCommitmentConfig, TableCommitmentWitness, TableDecommitment, TableUnsentCommitment + table_commitment::table_commitment::{ + TableCommitmentConfig, TableCommitmentWitness, TableDecommitment }, }; @@ -54,10 +54,7 @@ struct TracesUnsentCommitmentWithSerde { } impl IntoTracesUnsentCommitment of Into { fn into(self: TracesUnsentCommitmentWithSerde) -> TracesUnsentCommitment { - TracesUnsentCommitment { - original: TableUnsentCommitment { vector: self.original }, - interaction: TableUnsentCommitment { vector: self.original }, - } + TracesUnsentCommitment { original: self.original, interaction: self.original, } } } diff --git a/src/fri/fri.cairo b/src/fri/fri.cairo index 0cabc102f..1350fb581 100644 --- a/src/fri/fri.cairo +++ b/src/fri/fri.cairo @@ -6,9 +6,9 @@ use cairo_verifier::{ fri_layer::{FriLayerQuery, FriLayerComputationParams, compute_next_layer}, fri_last_layer::verify_last_layer, }, - table_commitment::{ + table_commitment::table_commitment::{ TableCommitmentWitness, TableDecommitment, TableCommitment, TableCommitmentConfig, - TableUnsentCommitment, table_commit, table_decommit + table_commit, table_decommit } }; @@ -17,7 +17,7 @@ use cairo_verifier::{ #[derive(Drop, Copy)] struct FriUnsentCommitment { // Array of size n_layers - 1 containing unsent table commitments for each inner layer. - inner_layers: Span, + inner_layers: Span, // Array of size 2**log_last_layer_degree_bound containing coefficients for the last layer // polynomial. last_layer_coefficients: Span, @@ -86,7 +86,7 @@ fn fri_commit_rounds( ref channel: Channel, n_layers: felt252, configs: Span, - unsent_commitments: Span, + unsent_commitments: Span, step_sizes: Span, ) -> (Array, Array) { let mut commitments = ArrayTrait::::new(); @@ -99,7 +99,7 @@ fn fri_commit_rounds( break; } // Read commitments. - commitments.append(table_commit(*unsent_commitments.at(i), *configs.at(i))); + commitments.append(table_commit(ref channel, *unsent_commitments.at(i), *configs.at(i))); // Send the next eval_points. eval_points.append(channel.random_felt_to_prover()); diff --git a/src/fri/fri_config.cairo b/src/fri/fri_config.cairo index 5ec9146aa..189f3153b 100644 --- a/src/fri/fri_config.cairo +++ b/src/fri/fri_config.cairo @@ -1,6 +1,6 @@ use cairo_verifier::{ common::{asserts::assert_in_range, math::{pow, Felt252PartialOrd}}, - table_commitment::TableCommitmentConfig, + table_commitment::table_commitment::TableCommitmentConfig, vector_commitment::vector_commitment::{VectorCommitmentConfig, VectorCommitmentConfigTrait}, }; diff --git a/src/oods.cairo b/src/oods.cairo index f2c2307ef..257eb8354 100644 --- a/src/oods.cairo +++ b/src/oods.cairo @@ -5,7 +5,7 @@ use cairo_verifier::air::composition::{eval_composition_polynomial, eval_oods_po use cairo_verifier::air::global_values::InteractionElements; use cairo_verifier::air::public_input::PublicInput; use cairo_verifier::air::traces::TracesDecommitment; -use cairo_verifier::table_commitment::TableDecommitment; +use cairo_verifier::table_commitment::table_commitment::TableDecommitment; use cairo_verifier::air::constants::CONSTRAINT_DEGREE; #[derive(Drop)] diff --git a/src/stark.cairo b/src/stark.cairo index 31766cd94..443e5ed70 100644 --- a/src/stark.cairo +++ b/src/stark.cairo @@ -5,7 +5,9 @@ use cairo_verifier::{ }, fri::{fri_config::{FriConfig, FriConfigTrait}, fri::{FriUnsentCommitment, FriWitness}}, domains::StarkDomainsImpl, - table_commitment::{TableCommitmentConfig, TableCommitmentWitness, TableDecommitment}, + table_commitment::table_commitment::{ + TableCommitmentConfig, TableCommitmentWitness, TableDecommitment + }, proof_of_work::{ config::{ProofOfWorkConfig, ProofOfWorkConfigTrait}, proof_of_work::ProofOfWorkUnsentCommitment diff --git a/src/table_commitment.cairo b/src/table_commitment.cairo index 79775baa4..140a6df47 100644 --- a/src/table_commitment.cairo +++ b/src/table_commitment.cairo @@ -1,67 +1,4 @@ -use cairo_verifier::vector_commitment::vector_commitment::{ - VectorCommitmentConfig, VectorCommitment, VectorCommitmentWitness -}; +mod table_commitment; -// Commitment values for a table commitment protocol. Used to generate a commitment by "reading" -// these values from the channel. -#[derive(Drop, Copy)] -struct TableUnsentCommitment { - vector: felt252, -} - -// Commitment for a table (n_rows x n_columns) of field elements in montgomery form. -#[derive(Drop, Copy)] -struct TableCommitment { - config: TableCommitmentConfig, - vector_commitment: VectorCommitment, -} - -#[derive(Drop, Copy)] -struct TableCommitmentConfig { - n_columns: felt252, - vector: VectorCommitmentConfig, -} - -// Responses for queries to the table commitment. -// Each query corresponds to a full row of the table. -#[derive(Drop, Copy)] -struct TableDecommitment { - // n_columns * n_queries values to decommit. - values: Span, -} - -// Witness for a decommitment over queries. -#[derive(Drop, Copy)] -struct TableCommitmentWitness { - vector: VectorCommitmentWitness, -} - -fn table_commit( - unsent_commitment: TableUnsentCommitment, config: TableCommitmentConfig -) -> TableCommitment { - TableCommitment { - config: TableCommitmentConfig { - n_columns: 0, - vector: VectorCommitmentConfig { height: 0, n_verifier_friendly_commitment_layers: 0, } - }, - vector_commitment: VectorCommitment { - config: VectorCommitmentConfig { height: 0, n_verifier_friendly_commitment_layers: 0, }, - commitment_hash: 0 - } - } -} - -// Decommits a TableCommitment at multiple indices. -// rows must be sorted and unique. -// Args: -// commitment - the table commitment. -// n_queries - number of queries to decommit. -// queries - the claimed indices. -// decommitment - the claimed values at those indices. -// witness - the decommitment witness. -fn table_decommit( - commitment: TableCommitment, - queries: Span, - decommitment: TableDecommitment, - witness: TableCommitmentWitness, -) {} +#[cfg(test)] +mod tests; diff --git a/src/table_commitment/table_commitment.cairo b/src/table_commitment/table_commitment.cairo new file mode 100644 index 000000000..2bc2a5de8 --- /dev/null +++ b/src/table_commitment/table_commitment.cairo @@ -0,0 +1,147 @@ +use cairo_verifier::common::flip_endianness::FlipEndiannessTrait; +use cairo_verifier::common::array_append::ArrayAppendTrait; +use cairo_verifier::vector_commitment::vector_commitment::{ + VectorCommitmentConfig, VectorCommitment, VectorCommitmentWitness, vector_commit, VectorQuery, + vector_commitment_decommit +}; +use cairo_verifier::channel::channel::Channel; +use cairo_verifier::common::math::Felt252PartialOrd; +use cairo_verifier::common::consts::MONTGOMERY_R; +use cairo_verifier::common::blake2s::blake2s; +use poseidon::poseidon_hash_span; + + +// Commitment for a table (n_rows x n_columns) of field elements in montgomery form. +#[derive(Drop, Copy)] +struct TableCommitment { + config: TableCommitmentConfig, + vector_commitment: VectorCommitment, +} + +#[derive(Drop, Copy)] +struct TableCommitmentConfig { + n_columns: felt252, + vector: VectorCommitmentConfig, +} + +// Responses for queries to the table commitment. +// Each query corresponds to a full row of the table. +#[derive(Drop, Copy)] +struct TableDecommitment { + // n_columns * n_queries values to decommit. + values: Span, +} + +// Witness for a decommitment over queries. +#[derive(Drop, Copy)] +struct TableCommitmentWitness { + vector: VectorCommitmentWitness, +} + +fn table_commit( + ref channel: Channel, unsent_commitment: felt252, config: TableCommitmentConfig +) -> TableCommitment { + let vector_commitment = vector_commit(ref channel, unsent_commitment, config.vector); + TableCommitment { config: config, vector_commitment: vector_commitment, } +} + +// Decommits a TableCommitment at multiple indices. +// rows must be sorted and unique. +// Args: +// commitment - the table commitment. +// n_queries - number of queries to decommit. +// queries - the claimed indices. +// decommitment - the claimed values at those indices. +// witness - the decommitment witness. +fn table_decommit( + commitment: TableCommitment, + queries: Span, + decommitment: TableDecommitment, + witness: TableCommitmentWitness, +) { + let n_queries: felt252 = queries.len().into(); + + // Determine if the table commitment should use a verifier friendly hash function for the bottom + // layer. The other layers' hash function will be determined in the vector_commitment logic. + let n_verifier_friendly_layers = commitment + .vector_commitment + .config + .n_verifier_friendly_commitment_layers; + + // An extra layer is added to the height since the table is considered as a layer, which is not + // included in vector_commitment.config. + let bottom_layer_depth = commitment.vector_commitment.config.height + 1; + let is_bottom_layer_verifier_friendly = n_verifier_friendly_layers >= bottom_layer_depth; + + // Must have at least 1 column + let n_columns = commitment.config.n_columns; + assert(n_columns >= 1, 'Must have at least 1 column'); + + assert( + decommitment.values.len().into() == n_queries * n_columns, 'Invalid decommitment length' + ); + + // Convert decommitment values to Montgomery form, since the commitment is in that form. + let montgomery_values = to_montgomery(decommitment.values); + + // Generate queries to the underlying vector commitment. + let vector_queries = generate_vector_queries( + queries, + montgomery_values.span(), + n_columns.try_into().unwrap(), + is_bottom_layer_verifier_friendly + ); + + vector_commitment_decommit(commitment.vector_commitment, vector_queries.span(), witness.vector); +} + +fn to_montgomery(arr: Span) -> Array { + let mut res = ArrayTrait::new(); + let mut i = 0; + loop { + if i == arr.len() { + break; + }; + res.append((*arr[i]) * MONTGOMERY_R); + i += 1; + }; + res +} + +fn generate_vector_queries( + queries: Span, values: Span, n_columns: u32, is_verifier_friendly: bool +) -> Array { + let queries_len = queries.len(); + let mut vector_queries = ArrayTrait::new(); + if queries_len == 0 { + return vector_queries; + } + let mut i = 0; + loop { + if i == queries_len { + break; + } + let hash = if n_columns == 1 { + *values[i * n_columns] + } else { + let slice = values.slice(i * n_columns, n_columns); + if is_verifier_friendly { + poseidon_hash_span(slice) + } else { + let mut data: Array = ArrayTrait::new(); + data.append_big_endian(slice); + + // Truncate hash - convert value to felt, by taking the 160 least significant bits + let hash: felt252 = (blake2s(data) + .flip_endianness() % 0x10000000000000000000000000000000000000000) + .try_into() + .unwrap(); + + hash + } + }; + vector_queries.append(VectorQuery { index: *queries[i], value: hash }); + i += 1; + }; + vector_queries +} diff --git a/src/table_commitment/tests.cairo b/src/table_commitment/tests.cairo new file mode 100644 index 000000000..954d81e66 --- /dev/null +++ b/src/table_commitment/tests.cairo @@ -0,0 +1 @@ +mod test_table_commitment; diff --git a/src/table_commitment/tests/test_table_commitment.cairo b/src/table_commitment/tests/test_table_commitment.cairo new file mode 100644 index 000000000..ed6b3002e --- /dev/null +++ b/src/table_commitment/tests/test_table_commitment.cairo @@ -0,0 +1,43 @@ +use cairo_verifier::table_commitment::table_commitment::{ + table_decommit, TableCommitment, TableCommitmentConfig, TableDecommitment, + TableCommitmentWitness +}; +use cairo_verifier::vector_commitment::vector_commitment::{ + VectorCommitment, VectorCommitmentConfig, VectorCommitmentWitness +}; + +#[test] +#[available_gas(9999999999)] +fn test_table_commitment_decommit() { + let commitment = TableCommitment { + vector_commitment: VectorCommitment { + config: VectorCommitmentConfig { height: 5, n_verifier_friendly_commitment_layers: 2, }, + commitment_hash: 0x65AB11B61229977B507F7B37C4E95769A0F9F8939042B2029D92CFF0D96FD07, + }, + config: TableCommitmentConfig { + n_columns: 4, + vector: VectorCommitmentConfig { height: 5, n_verifier_friendly_commitment_layers: 2, }, + } + }; + + let queries = array![1, 4]; + let values = array![10, 10, 10, 10, 20, 20, 20, 20]; + + let decommitment = TableDecommitment { values: values.span() }; + + let witness = TableCommitmentWitness { + vector: VectorCommitmentWitness { + authentications: array![ + 2722258935367507707706996859454145691656, + 2722258935367507707706996859454145691656, + 2722258935367507707706996859454145691656, + 2722258935367507707706996859454145691656, + 2722258935367507707706996859454145691656, + 2722258935367507707706996859454145691656 + ] + .span() + } + }; + + table_decommit(commitment, queries.span(), decommitment, witness); +}