diff --git a/src/common.cairo b/src/common.cairo index 95b44e817..8083003bd 100644 --- a/src/common.cairo +++ b/src/common.cairo @@ -3,6 +3,7 @@ mod flip_endiannes; mod from_span; mod horner_eval; mod to_array; +mod math; #[cfg(test)] mod tests; diff --git a/src/common/math.cairo b/src/common/math.cairo new file mode 100644 index 000000000..b81f22ccb --- /dev/null +++ b/src/common/math.cairo @@ -0,0 +1,22 @@ +fn pow(base: felt252, exp: felt252) -> felt252 { + if exp == 0 { + return 1; // Return 1 for zero exponent + } + let mut exp: u256 = exp.into(); + let mut res = 1; // Initialize result as 1 + let mut curr_base = base; // Current base value + + loop { + if exp == 0 { + break; + } else { + if exp % 2 == 1 { + res = res * curr_base; // Multiply result only when exp is odd + } + curr_base = curr_base * curr_base; // Square the base for next iteration + exp = exp / 2; // Divide exponent by 2 + } + }; + + res +} diff --git a/src/common/tests.cairo b/src/common/tests.cairo index d944d16ca..c59c48eb2 100644 --- a/src/common/tests.cairo +++ b/src/common/tests.cairo @@ -3,3 +3,4 @@ mod test_flip_endiannes; mod test_from_span; mod test_horner_eval; mod test_to_array; +mod test_math; diff --git a/src/common/tests/test_math.cairo b/src/common/tests/test_math.cairo new file mode 100644 index 000000000..9801bea41 --- /dev/null +++ b/src/common/tests/test_math.cairo @@ -0,0 +1,27 @@ +use cairo_verifier::common::math::pow; + +#[test] +#[available_gas(9999999999)] +fn test_pow_1() { + let base = 1934568044210770965733097210694395167600009938751278224656090409051406060084; + let exp = 69439516760000993875127; + assert( + pow( + base, exp + ) == 2804690217475462062143361339624939640984649667966511418446363596075299761851, + 'Invalid value' + ); +} + +#[test] +#[available_gas(9999999999)] +fn test_pow_2() { + let base = 193456804421077096570009938751278224656090409051406060084; + let exp = 193456804421077096570009938751278224656090409051406060084; + assert( + pow( + base, exp + ) == 2672162222334975109199941471365701890765112108683608796920114577809390903720, + 'Invalid value' + ); +} diff --git a/src/fri/fri_config.cairo b/src/fri/fri_config.cairo index 1bb8011df..a6e942811 100644 --- a/src/fri/fri_config.cairo +++ b/src/fri/fri_config.cairo @@ -9,6 +9,7 @@ const MAX_LAST_LAYER_LOG_DEGREE_BOUND: u32 = 15; const MAX_FRI_LAYERS: u32 = 15; const MAX_FRI_STEP: u32 = 4; +#[derive(Drop)] struct FriConfig { // Log2 of the size of the input layer to FRI. log_input_size: felt252, diff --git a/src/fri/fri_layer.cairo b/src/fri/fri_layer.cairo index 2d9432d39..c0037dfbd 100644 --- a/src/fri/fri_layer.cairo +++ b/src/fri/fri_layer.cairo @@ -1,13 +1,17 @@ use core::array::SpanTrait; use core::array::ArrayTrait; +use cairo_verifier::fri::fri_formula::fri_formula; +use cairo_verifier::common::math; // Constant parameters for computing the next FRI layer. +#[derive(Drop, Copy)] struct FriLayerComputationParams { coset_size: felt252, fri_group: Span, eval_point: felt252, } +#[derive(Drop, Copy)] struct FriLayerQuery { index: felt252, y_value: felt252, @@ -40,7 +44,7 @@ fn compute_coset_elements( fri_group: Span, ) -> (Array, felt252) { let mut coset_elements = ArrayTrait::::new(); - let mut coset_x_inv: felt252 = 1; + let mut coset_x_inv: felt252 = 0; let mut i: u32 = 0; let mut j: u32 = 0; @@ -63,3 +67,81 @@ fn compute_coset_elements( (coset_elements, coset_x_inv) } + +// Computes FRI next layer for the given queries. I.e., takes the given i-th layer queries +// and produces queries for layer i+1 (a single query for each coset in the i-th layer). +// +// Inputs: +// - n_queries: the number of input queries. +// - queries: input queries. +// - sibling_witness: a list of all the query's siblings. +// - params: the parameters to use for the layer computation. +// +// Outputs: +// - next_queries: queries for the next layer. +// - verify_indices: query indices of the given layer for Merkle verification. +// - verify_y_values: query y values of the given layer for Merkle verification. +fn compute_next_layer( + mut n_queries: felt252, + queries: Span, + sibling_witness: Span, + params: FriLayerComputationParams, +) -> (Array, Array, Array) { + let mut next_queries = ArrayTrait::::new(); + let mut verify_indices = ArrayTrait::::new(); + let mut verify_y_values = ArrayTrait::::new(); + + let coset_size = params.coset_size; + + let mut i: u32 = 0; + loop { + if n_queries == 0 { + break; + } + + let coset_index = *(queries.at(i)).index; + assert(0_u256 <= coset_index.into(), 'Invalid value'); + + verify_indices.append(coset_index); + + let (coset_elements, coset_x_inv) = compute_coset_elements( + n_queries, + queries, + sibling_witness, + coset_size, + coset_index * coset_size, + 0, + params.fri_group + ); + + let coset_elements_len = coset_elements.len(); + assert(0 <= coset_elements_len, 'Invalid value'); + + let coset_elements_span = coset_elements.span(); + + let mut j: u32 = 0; + loop { + if j == coset_elements_len { + break; + } + verify_y_values.append(*(coset_elements_span.at(j))); + }; + + let fri_formula_res = fri_formula( + coset_elements_span, params.eval_point, coset_x_inv, coset_size, + ); + + let next_x_inv = math::pow(coset_x_inv, params.coset_size); + + next_queries + .append( + FriLayerQuery { + index: coset_index, y_value: fri_formula_res, x_inv_value: next_x_inv + } + ); + + i += 1; + }; + + (next_queries, verify_indices, verify_y_values) +}