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

Enhancement: Introducing Dynamic FRI Folding Schedules #212

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
10 changes: 7 additions & 3 deletions air/src/air/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use super::{
};
use crate::{AuxTraceRandElements, FieldExtension};
use crypto::{hashers::Blake3_256, DefaultRandomCoin, RandomCoin};
use fri::fri_schedule::FoldingSchedule;
use math::{fields::f64::BaseElement, get_power_series, polynom, FieldElement, StarkField};
use utils::collections::{BTreeMap, Vec};

Expand Down Expand Up @@ -219,20 +220,22 @@ impl MockAir {
column_values: Vec<Vec<BaseElement>>,
trace_length: usize,
) -> Self {
let fri_constant_schedule = FoldingSchedule::new_constant(4, 31);
let mut result = Self::new(
TraceInfo::with_meta(4, trace_length, vec![1]),
(),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31),
ProofOptions::new(32, 8, 0, FieldExtension::None, &fri_constant_schedule),
);
result.periodic_columns = column_values;
result
}

pub fn with_assertions(assertions: Vec<Assertion<BaseElement>>, trace_length: usize) -> Self {
let fri_constant_schedule = FoldingSchedule::new_constant(4, 31);
let mut result = Self::new(
TraceInfo::with_meta(4, trace_length, vec![assertions.len() as u8]),
(),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31),
ProofOptions::new(32, 8, 0, FieldExtension::None, &fri_constant_schedule),
);
result.assertions = assertions;
result
Expand Down Expand Up @@ -282,7 +285,8 @@ pub fn build_context<B: StarkField>(
trace_width: usize,
num_assertions: usize,
) -> AirContext<B> {
let options = ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31);
let fri_constant_schedule = FoldingSchedule::new_constant(4, 31);
let options = ProofOptions::new(32, 8, 0, FieldExtension::None, &fri_constant_schedule);
let t_degrees = vec![TransitionConstraintDegree::new(2)];
let trace_info = TraceInfo::new(trace_width, trace_length);
AirContext::new(trace_info, t_degrees, num_assertions, options)
Expand Down
92 changes: 59 additions & 33 deletions air/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

use fri::FriOptions;
use fri::{fri_schedule::FoldingSchedule, FriOptions};
use math::{StarkField, ToElements};
use utils::{
collections::Vec, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
Expand Down Expand Up @@ -69,6 +69,10 @@ pub enum FieldExtension {
/// 4. Grinding factor - higher values increase proof soundness, but also may increase proof
/// generation time. More precisely, conjectured proof soundness is bounded by
/// `num_queries * log2(blowup_factor) + grinding_factor`.
/// 5. FRI Schedule - The strategy for the FRI reduction process, which can be either a constant
/// folding factor or a dynamic schedule of factors. This setting influences the FRI proof
/// generation process, potentially affecting both the proof generation time and the resultant
/// proof size.
///
/// Another important parameter in defining STARK security level, which is not a part of [ProofOptions]
/// is the hash function used in the protocol. The soundness of a STARK proof is limited by the
Expand All @@ -80,8 +84,7 @@ pub struct ProofOptions {
blowup_factor: u8,
grinding_factor: u8,
field_extension: FieldExtension,
fri_folding_factor: u8,
fri_remainder_max_degree: u8,
fri_schedule: FoldingSchedule,
}

// PROOF OPTIONS IMPLEMENTATION
Expand All @@ -106,16 +109,16 @@ impl ProofOptions {
/// - `num_queries` is zero or greater than 255.
/// - `blowup_factor` is smaller than 2, greater than 128, or is not a power of two.
/// - `grinding_factor` is greater than 32.
/// - `fri_folding_factor` is not 2, 4, 8, or 16.
/// - `fri_remainder_max_degree` is greater than 255 or is not a power of two minus 1.
/// - `fri_folding_schedule` is not 2, 4, 8, or 16 in case of a constant FRI schedule, or is not
/// a power of two in case of a dynamic FRI schedule. In the case of constant FRI schedule, the
/// max remainder degree must be less than 255 and must be one less than a power of two.
#[rustfmt::skip]
pub fn new(
num_queries: usize,
blowup_factor: usize,
grinding_factor: u32,
field_extension: FieldExtension,
fri_folding_factor: usize,
fri_remainder_max_degree: usize,
fri_folding_schedule: &FoldingSchedule,
) -> ProofOptions {
// TODO: return errors instead of panicking
assert!(num_queries > 0, "number of queries must be greater than 0");
Expand All @@ -127,26 +130,36 @@ impl ProofOptions {

assert!(grinding_factor <= MAX_GRINDING_FACTOR, "grinding factor cannot be greater than {MAX_GRINDING_FACTOR}");

assert!(fri_folding_factor.is_power_of_two(), "FRI folding factor must be a power of 2");
assert!(fri_folding_factor >= FRI_MIN_FOLDING_FACTOR, "FRI folding factor cannot be smaller than {FRI_MIN_FOLDING_FACTOR}");
assert!(fri_folding_factor <= FRI_MAX_FOLDING_FACTOR, "FRI folding factor cannot be greater than {FRI_MAX_FOLDING_FACTOR}");

assert!(
(fri_remainder_max_degree + 1).is_power_of_two(),
"FRI polynomial remainder degree must be one less than a power of two"
);
assert!(
fri_remainder_max_degree <= FRI_MAX_REMAINDER_DEGREE,
"FRI polynomial remainder degree cannot be greater than {FRI_MAX_REMAINDER_DEGREE}"
);
// let fri_folding_factor = fri_folding_schedule.get_factor().unwrap_or(3);
// let fri_remainder_max_degree = fri_folding_schedule.get_max_remainder_degree().unwrap_or(127);
match fri_folding_schedule {
FoldingSchedule::Constant { fri_folding_factor, fri_remainder_max_degree } => {
assert!(fri_folding_factor.is_power_of_two(), "FRI folding factor must be a power of 2");
assert!(*fri_folding_factor as usize >= FRI_MIN_FOLDING_FACTOR, "FRI folding factor cannot be smaller than {FRI_MIN_FOLDING_FACTOR}");
assert!(*fri_folding_factor as usize <= FRI_MAX_FOLDING_FACTOR, "FRI folding factor cannot be greater than {FRI_MAX_FOLDING_FACTOR}");

assert!(
(fri_remainder_max_degree + 1).is_power_of_two(),
"FRI polynomial remainder degree must be one less than a power of two"
);
assert!(
*fri_remainder_max_degree as usize <= FRI_MAX_REMAINDER_DEGREE,
"FRI polynomial remainder degree cannot be greater than {FRI_MAX_REMAINDER_DEGREE}"
);

},
FoldingSchedule::Dynamic { schedule} => {
assert!(schedule.iter().all(|factor| factor.is_power_of_two()), "FRI folding factors must be powers of 2");
assert!(!schedule.is_empty(), "FRI folding schedule cannot be empty");
},
}

ProofOptions {
num_queries: num_queries as u8,
blowup_factor: blowup_factor as u8,
grinding_factor: grinding_factor as u8,
field_extension,
fri_folding_factor: fri_folding_factor as u8,
fri_remainder_max_degree: fri_remainder_max_degree as u8,
fri_schedule: fri_folding_schedule.clone(),
}
}

Expand Down Expand Up @@ -202,18 +215,29 @@ impl ProofOptions {

/// Returns options for FRI protocol instantiated with parameters from this proof options.
pub fn to_fri_options(&self) -> FriOptions {
let folding_factor = self.fri_folding_factor as usize;
let remainder_max_degree = self.fri_remainder_max_degree as usize;
FriOptions::new(self.blowup_factor(), folding_factor, remainder_max_degree)
FriOptions::new(self.blowup_factor(), self.fri_schedule.clone())
}
}

impl<E: StarkField> ToElements<E> for ProofOptions {
fn to_elements(&self) -> Vec<E> {
// encode field extension and FRI parameters into a single field element
let mut buf = self.field_extension as u32;
buf = (buf << 8) | self.fri_folding_factor as u32;
buf = (buf << 8) | self.fri_remainder_max_degree as u32;
match &self.fri_schedule {
FoldingSchedule::Constant {
fri_folding_factor,
fri_remainder_max_degree,
} => {
buf = (buf << 8) | *fri_folding_factor as u32;
buf = (buf << 8) | *fri_remainder_max_degree as u32;
}
FoldingSchedule::Dynamic { schedule } => {
buf = (buf << 8) | schedule.len() as u32;
for factor in schedule {
buf = (buf << 8) | *factor as u32;
}
}
}

vec![
E::from(buf),
Expand All @@ -231,8 +255,7 @@ impl Serializable for ProofOptions {
target.write_u8(self.blowup_factor);
target.write_u8(self.grinding_factor);
target.write(self.field_extension);
target.write_u8(self.fri_folding_factor);
target.write_u8(self.fri_remainder_max_degree);
target.write(self.fri_schedule.clone());
}
}

Expand All @@ -247,8 +270,7 @@ impl Deserializable for ProofOptions {
source.read_u8()? as usize,
source.read_u8()? as u32,
FieldExtension::read_from(source)?,
source.read_u8()? as usize,
source.read_u8()? as usize,
&FoldingSchedule::read_from(source)?,
))
}
}
Expand Down Expand Up @@ -299,13 +321,18 @@ impl Deserializable for FieldExtension {
#[cfg(test)]
mod tests {
use super::{FieldExtension, ProofOptions, ToElements};
use fri::fri_schedule::FoldingSchedule;
use math::fields::f64::BaseElement;

#[test]
fn proof_options_to_elements() {
let field_extension = FieldExtension::None;
let fri_folding_factor = 8;
let fri_remainder_max_degree = 127;
let fri_folding_schedule = FoldingSchedule::Constant {
fri_folding_factor,
fri_remainder_max_degree,
};
let grinding_factor = 20;
let blowup_factor = 8;
let num_queries = 30;
Expand All @@ -318,7 +345,7 @@ mod tests {
]);
let expected = vec![
BaseElement::from(ext_fri),
BaseElement::from(grinding_factor as u32),
BaseElement::from(grinding_factor),
BaseElement::from(blowup_factor as u32),
BaseElement::from(num_queries as u32),
];
Expand All @@ -328,8 +355,7 @@ mod tests {
blowup_factor,
grinding_factor,
field_extension,
fri_folding_factor as usize,
fri_remainder_max_degree as usize,
&fri_folding_schedule,
);
assert_eq!(expected, options.to_elements());
}
Expand Down
9 changes: 6 additions & 3 deletions air/src/proof/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ fn bytes_to_element<B: StarkField>(bytes: &[u8]) -> B {
mod tests {
use super::{Context, ProofOptions, ToElements, TraceInfo};
use crate::{FieldExtension, TraceLayout};
use fri::fri_schedule::FoldingSchedule;
use math::fields::f64::BaseElement;

#[test]
Expand All @@ -247,14 +248,17 @@ mod tests {
0,
]);

let fri_constant_schedule =
FoldingSchedule::new_constant(fri_folding_factor, fri_remainder_max_degree);

let layout_info = u32::from_le_bytes([aux_rands, aux_width, num_aux_segments, main_width]);

let expected = vec![
BaseElement::from(layout_info),
BaseElement::from(1_u32), // lower bits of field modulus
BaseElement::from(u32::MAX), // upper bits of field modulus
BaseElement::from(ext_fri),
BaseElement::from(grinding_factor as u32),
BaseElement::from(grinding_factor),
BaseElement::from(blowup_factor as u32),
BaseElement::from(num_queries as u32),
BaseElement::from(trace_length as u32),
Expand All @@ -265,8 +269,7 @@ mod tests {
blowup_factor,
grinding_factor,
field_extension,
fri_folding_factor as usize,
fri_remainder_max_degree as usize,
&fri_constant_schedule,
);
let layout = TraceLayout::new(
main_width as usize,
Expand Down
2 changes: 1 addition & 1 deletion crypto/src/hash/griffin/griffin64_256_jive/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ proptest! {
for i in 0..STATE_WIDTH {
v1[i] = BaseElement::new(a[i]);
}
v2 = v1.clone();
v2 = v1;

apply_mds_naive(&mut v1);
GriffinJive64_256::apply_linear(&mut v2);
Expand Down
2 changes: 1 addition & 1 deletion crypto/src/hash/rescue/rp64_256/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ proptest! {
for i in 0..STATE_WIDTH {
v1[i] = BaseElement::new(a[i]);
}
v2 = v1.clone();
v2 = v1;

apply_mds_naive(&mut v1);
Rp64_256::apply_mds(&mut v2);
Expand Down
4 changes: 2 additions & 2 deletions crypto/src/hash/rescue/rp64_256_jive/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn mds_inv_test() {
#[test]
fn test_alphas() {
let e: BaseElement = rand_value();
let e_exp = e.exp(ALPHA.into());
let e_exp = e.exp(ALPHA);
assert_eq!(e, e_exp.exp(INV_ALPHA));
}

Expand Down Expand Up @@ -197,7 +197,7 @@ proptest! {
for i in 0..STATE_WIDTH {
v1[i] = BaseElement::new(a[i]);
}
v2 = v1.clone();
v2 = v1;

apply_mds_naive(&mut v1);
RpJive64_256::apply_mds(&mut v2);
Expand Down
1 change: 1 addition & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ std = ["hex/std", "winterfell/std", "core-utils/std", "rand-utils"]

[dependencies]
winterfell = { version="0.6", path = "../winterfell", default-features = false }
winter-fri = { version="0.6", path = "../fri", default-features = false }
core-utils = { version = "0.6", path = "../utils/core", package = "winter-utils", default-features = false }
rand-utils = { version = "0.6", path = "../utils/rand", package = "winter-rand-utils", optional = true }
hex = { version = "0.4", optional = true }
Expand Down
5 changes: 4 additions & 1 deletion examples/benches/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use examples::{fibonacci, Example};
use std::time::Duration;
use winter_fri::fri_schedule::FoldingSchedule;
use winterfell::{
crypto::hashers::Blake3_256, math::fields::f128::BaseElement, FieldExtension, ProofOptions,
};
Expand All @@ -17,7 +18,9 @@ fn fibonacci(c: &mut Criterion) {
group.sample_size(10);
group.measurement_time(Duration::from_secs(20));

let options = ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 255);
let fri_constant_schedule = FoldingSchedule::new_constant(4, 255);

let options = ProofOptions::new(32, 8, 0, FieldExtension::None, &fri_constant_schedule);

for &size in SIZES.iter() {
let fib =
Expand Down
5 changes: 4 additions & 1 deletion examples/benches/rescue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use examples::{rescue, Example};
use winter_fri::fri_schedule::FoldingSchedule;

use std::time::Duration;
use winterfell::{
Expand All @@ -18,7 +19,9 @@ fn rescue(c: &mut Criterion) {
group.sample_size(10);
group.measurement_time(Duration::from_secs(25));

let options = ProofOptions::new(32, 32, 0, FieldExtension::None, 4, 255);
let fri_constant_schedule = FoldingSchedule::new_constant(4, 255);

let options = ProofOptions::new(32, 32, 0, FieldExtension::None, &fri_constant_schedule);

for &size in SIZES.iter() {
let resc = rescue::RescueExample::<Blake3_256<BaseElement>>::new(size, options.clone());
Expand Down
5 changes: 4 additions & 1 deletion examples/src/fibonacci/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ pub fn compute_mulfib_term(n: usize) -> BaseElement {

#[cfg(test)]
pub fn build_proof_options(use_extension_field: bool) -> winterfell::ProofOptions {
use winter_fri::fri_schedule::FoldingSchedule;
use winterfell::{FieldExtension, ProofOptions};

let extension = if use_extension_field {
FieldExtension::Quadratic
} else {
FieldExtension::None
};
ProofOptions::new(28, 8, 0, extension, 4, 7)

let fri_constant_schedule = FoldingSchedule::new_constant(4, 7);
ProofOptions::new(28, 8, 0, extension, &fri_constant_schedule)
}
Loading