From 34116068c3b1952b7cb47363c2c61f91d47d0d3c Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Fri, 23 Feb 2024 21:25:15 +0100 Subject: [PATCH 1/2] Feature: generate FRI parameters compatible with L1 verifier Problem: the FRI parameters generation is not compatible with the L1 verifier. The step list needs to start with 0 and the last entry cannot be 1. Solution: implement an alternative computation method for the L1 verifier and allow the user to select his verifier. --- src/fri.rs | 142 +++++++++++++++++++++++++++++++++++++++++--------- src/models.rs | 6 +++ 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index fca1f2e..3a133c2 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -1,4 +1,8 @@ -use crate::models::{FriParameters, ProverParameters, StarkParameters}; +use crate::models::{FriParameters, ProverParameters, StarkParameters, Verifier}; + +const DEFAULT_LAST_LAYER_DEGREE_BOUND: u32 = 64; +const DEFAULT_N_QUERIES: u32 = 18; +const DEFAULT_PROOF_OF_WORK_BITS: u32 = 24; /// Implements ceil(log2(x)). fn ceil_log2(x: u32) -> u32 { @@ -9,26 +13,29 @@ fn ceil_log2(x: u32) -> u32 { log } -/// Computes the FRI steps list based on the number of Cairo steps of the program. +/// Computes the FRI steps list based on the specified parameters. /// /// This computation is based on the documentation of the Stone prover: /// # log₂(#steps) + 4 = log₂(last_layer_degree_bound) + ∑fri_step_list /// # log₂(#steps) = log₂(last_layer_degree_bound) + ∑fri_step_list - 4 /// # ∑fri_step_list = log₂(#steps) + 4 - log₂(last_layer_degree_bound) /// -/// * `nb_steps`: Number of Cairo steps of the program. -/// * `last_layer_degree_bound`: Last layer degree bound. +/// * `nb_steps_log`: Ceiled log₂ of the number of Cairo steps of the program. +/// * `last_layer_degree_bound_log`: Ceiled log₂ of the last layer degree bound. +/// * `max_step_value`: Maximum value for each step. All elements will be in the range +/// [0, `max_step_value`]. /// /// Returns The FRI steps list. -pub fn compute_fri_steps(nb_steps: u32, last_layer_degree_bound: u32) -> Vec { - let nb_steps_log = ceil_log2(nb_steps); - let last_layer_degree_bound_log = ceil_log2(last_layer_degree_bound); - - let sigma_fri_step_list = nb_steps_log + 4 - last_layer_degree_bound_log; - let quotient = (sigma_fri_step_list / 4) as usize; - let remainder = sigma_fri_step_list % 4; +fn compute_fri_steps( + nb_steps_log: u32, + last_layer_degree_bound_log: u32, + max_step_value: u32, +) -> Vec { + let sum_of_fri_steps = nb_steps_log + 4 - last_layer_degree_bound_log; + let quotient = (sum_of_fri_steps / max_step_value) as usize; + let remainder = sum_of_fri_steps % max_step_value; - let mut fri_steps = vec![4; quotient]; + let mut fri_steps = vec![max_step_value; quotient]; if remainder > 0 { fri_steps.push(remainder); } @@ -36,21 +43,84 @@ pub fn compute_fri_steps(nb_steps: u32, last_layer_degree_bound: u32) -> Vec FriParameters; +} + +pub struct DefaultFriComputer; + +impl FriComputer for DefaultFriComputer { + fn compute_fri_parameters(&self, nb_steps: u32) -> FriParameters { + let last_layer_degree_bound = 64; + + let nb_steps_log = ceil_log2(nb_steps); + let last_layer_degree_bound_log = ceil_log2(last_layer_degree_bound); + let max_step_value = 4; + + let fri_steps = + compute_fri_steps(nb_steps_log, last_layer_degree_bound_log, max_step_value); + + FriParameters { + fri_step_list: fri_steps, + last_layer_degree_bound, + n_queries: DEFAULT_N_QUERIES, + proof_of_work_bits: DEFAULT_PROOF_OF_WORK_BITS, + } + } +} + +pub struct L1VerifierFriComputer; + +impl FriComputer for L1VerifierFriComputer { + fn compute_fri_parameters(&self, nb_steps: u32) -> FriParameters { + // The L1 verifier accepts FRI steps in [0, 1, 2]. + let max_step_value = 2; + + let nb_steps_log = ceil_log2(nb_steps); + + let (last_layer_degree_bound, last_layer_degree_bound_log) = { + let mut lldb = DEFAULT_LAST_LAYER_DEGREE_BOUND; + // The last step cannot be 1, prevent this by reducing the last layer degree bound. + // Using log₂(#steps) + 4 = log₂(last_layer_degree_bound) + ∑fri_step_list, + // we just need log₂(#steps) - log₂(last_layer_degree_bound) to be even. + let mut lldb_log = ceil_log2(lldb); + if ((nb_steps_log - lldb_log) % 2) != 0 { + lldb /= 2; + lldb_log -= 1; + } + (lldb, lldb_log) + }; + + // The first FRI step must be 0 + let mut fri_steps = vec![0]; + fri_steps.extend(compute_fri_steps( + nb_steps_log, + last_layer_degree_bound_log, + max_step_value, + )); + + FriParameters { + fri_step_list: fri_steps, + last_layer_degree_bound, + n_queries: DEFAULT_N_QUERIES, + proof_of_work_bits: DEFAULT_PROOF_OF_WORK_BITS, + } + } +} + /// Generates prover parameters based on program parameters. /// /// * `nb_steps`: Number of Cairo steps of the program. /// * `last_layer_degree_bound`: Last layer degree bound. -pub fn generate_prover_parameters(nb_steps: u32, last_layer_degree_bound: u32) -> ProverParameters { - let fri_steps = compute_fri_steps(nb_steps, last_layer_degree_bound); +pub fn generate_prover_parameters(nb_steps: u32, verifier: Verifier) -> ProverParameters { + let fri_parameters = match verifier { + Verifier::L1 => L1VerifierFriComputer.compute_fri_parameters(nb_steps), + _ => DefaultFriComputer.compute_fri_parameters(nb_steps), + }; ProverParameters { field: "PrimeField0".to_string(), stark: StarkParameters { - fri: FriParameters { - fri_step_list: fri_steps, - last_layer_degree_bound, - n_queries: 18, - proof_of_work_bits: 24, - }, + fri: fri_parameters, log_n_cosets: 4, }, use_extension_field: false, @@ -76,9 +146,33 @@ mod tests { #[case(32768, vec ! [4, 4, 4, 1])] #[case(524288, vec ! [4, 4, 4, 4, 1])] #[case(768, vec ! [4, 4])] - fn test_compute_fri_step_list(#[case] nb_steps: u32, #[case] expected: Vec) { - let last_layer_degree_bound = 64; - let step_list = compute_fri_steps(nb_steps, last_layer_degree_bound); - assert_eq!(step_list, expected); + fn test_compute_fri_parameters_default(#[case] nb_steps: u32, #[case] expected: Vec) { + let expected_last_layer_degree_bound = 64; + let fri_parameters = DefaultFriComputer.compute_fri_parameters(nb_steps); + + assert_eq!(fri_parameters.fri_step_list, expected); + assert_eq!( + fri_parameters.last_layer_degree_bound, + expected_last_layer_degree_bound + ); + } + + /// # ∑fri_step_list = log₂(#steps) + 4 - log₂(last_layer_degree_bound) + #[rstest] + #[case(32768, vec ! [0, 2, 2, 2, 2, 2, 2, 2], 32)] + #[case(524288, vec ! [0, 2, 2, 2, 2, 2, 2, 2, 2, 2], 32)] + #[case(768, vec ! [0, 2, 2, 2, 2], 64)] + fn test_compute_fri_parameters_l1_verifier( + #[case] nb_steps: u32, + #[case] expected_fri_steps: Vec, + #[case] expected_last_layer_degree_bound: u32, + ) { + let fri_parameters = L1VerifierFriComputer.compute_fri_parameters(nb_steps); + + assert_eq!(fri_parameters.fri_step_list, expected_fri_steps); + assert_eq!( + fri_parameters.last_layer_degree_bound, + expected_last_layer_degree_bound + ); } } diff --git a/src/models.rs b/src/models.rs index 88aeb73..73c0141 100644 --- a/src/models.rs +++ b/src/models.rs @@ -8,6 +8,12 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; use serde_json::Value; +#[derive(Debug)] +pub enum Verifier { + Stone, + L1, +} + #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct CachedLdeConfig { pub store_full_lde: bool, From 4e86b70f245f02b8a76c7d7ad0ce93fe7f9a3400 Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Fri, 23 Feb 2024 23:32:14 +0100 Subject: [PATCH 2/2] impl FromStr + Display --- src/models.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/models.rs b/src/models.rs index 73c0141..4bcf772 100644 --- a/src/models.rs +++ b/src/models.rs @@ -8,12 +8,38 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; use serde_json::Value; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Verifier { Stone, L1, } +impl FromStr for Verifier { + type Err = String; + + fn from_str(s: &str) -> Result { + let verifier = match s { + "stone" => Self::Stone, + "l1" => Self::L1, + other => { + return Err(format!("unknown verifier: {other}")); + } + }; + + Ok(verifier) + } +} + +impl Display for Verifier { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Stone => "stone", + Self::L1 => "l1", + }; + write!(f, "{}", s) + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct CachedLdeConfig { pub store_full_lde: bool,