Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: generate FRI parameters compatible with L1 verifier #7

Merged
merged 2 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 118 additions & 24 deletions src/fri.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -9,48 +13,114 @@ 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<u32> {
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<u32> {
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);
}

fri_steps
}

pub trait FriComputer {
fn compute_fri_parameters(&self, nb_steps: u32) -> 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,
Expand All @@ -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<u32>) {
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<u32>) {
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<u32>,
#[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
);
}
}
32 changes: 32 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,38 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone)]
pub enum Verifier {
Stone,
L1,
}

impl FromStr for Verifier {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
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,
Expand Down
Loading