From ac127d5b72a28b4da97b142101e84ccb1fb8f51d Mon Sep 17 00:00:00 2001 From: Ohad <137686240+ohad-starkware@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:31:43 +0300 Subject: [PATCH 01/27] disable lfs tests & fetch (#126) --- .github/workflows/cairo-ci.yaml | 2 +- stwo_cairo_prover/.gitattributes | 9 --------- stwo_cairo_prover/crates/prover/src/cairo_air/mod.rs | 1 + .../crates/prover/src/input/vm_import/mod.rs | 3 ++- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cairo-ci.yaml b/.github/workflows/cairo-ci.yaml index de86c3de2..db66f6423 100644 --- a/.github/workflows/cairo-ci.yaml +++ b/.github/workflows/cairo-ci.yaml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - lfs: true + lfs: false - uses: dtolnay/rust-toolchain@master with: components: rustfmt diff --git a/stwo_cairo_prover/.gitattributes b/stwo_cairo_prover/.gitattributes index 8eb14593c..e69de29bb 100644 --- a/stwo_cairo_prover/.gitattributes +++ b/stwo_cairo_prover/.gitattributes @@ -1,9 +0,0 @@ -crates/prover/test_data/large_cairo_input/fact.json filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/large_cairo_input/mem filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/large_cairo_input/priv.json filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/large_cairo_input/pub.json filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/large_cairo_input/trace filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/small_cairo_input/mem filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/small_cairo_input/priv.json filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/small_cairo_input/pub.json filter=lfs diff=lfs merge=lfs -text -crates/prover/test_data/small_cairo_input/trace filter=lfs diff=lfs merge=lfs -text diff --git a/stwo_cairo_prover/crates/prover/src/cairo_air/mod.rs b/stwo_cairo_prover/crates/prover/src/cairo_air/mod.rs index 3036221d8..5206e323a 100644 --- a/stwo_cairo_prover/crates/prover/src/cairo_air/mod.rs +++ b/stwo_cairo_prover/crates/prover/src/cairo_air/mod.rs @@ -427,6 +427,7 @@ mod tests { verify_cairo(cairo_proof).unwrap(); } + #[ignore] #[test] fn test_full_cairo_air() { let cairo_proof = prove_cairo(small_cairo_input()).unwrap(); diff --git a/stwo_cairo_prover/crates/prover/src/input/vm_import/mod.rs b/stwo_cairo_prover/crates/prover/src/input/vm_import/mod.rs index 1bb7d849e..d3936a87c 100644 --- a/stwo_cairo_prover/crates/prover/src/input/vm_import/mod.rs +++ b/stwo_cairo_prover/crates/prover/src/input/vm_import/mod.rs @@ -150,7 +150,7 @@ pub mod tests { ) } - // Slow test. Run only in release. + #[ignore] #[test] fn test_read_from_large_files() { let input = large_cairo_input(); @@ -173,6 +173,7 @@ pub mod tests { println!("Usage: {:#?}", input.instructions.usage()); } + #[ignore] #[test] fn test_read_from_small_files() { let input = small_cairo_input(); From c3dcadffd9f754ba2374c4dbfc381f725717f1ca Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 14:10:19 -0400 Subject: [PATCH 02/27] Update to latest Cairo version (#112) --- .github/workflows/cairo-ci.yaml | 2 +- stwo_cairo_verifier/.tool-versions | 3 ++- stwo_cairo_verifier/Scarb.toml | 8 ++++++-- stwo_cairo_verifier/src/circle.cairo | 4 ++-- stwo_cairo_verifier/src/fields/cm31.cairo | 2 +- stwo_cairo_verifier/src/fields/m31.cairo | 2 +- stwo_cairo_verifier/src/fields/qm31.cairo | 2 +- stwo_cairo_verifier/src/fri.cairo | 1 + stwo_cairo_verifier/src/poly/circle.cairo | 4 ++-- stwo_cairo_verifier/src/queries.cairo | 2 +- stwo_cairo_verifier/src/utils.cairo | 1 + 11 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cairo-ci.yaml b/.github/workflows/cairo-ci.yaml index db66f6423..18e0284ad 100644 --- a/.github/workflows/cairo-ci.yaml +++ b/.github/workflows/cairo-ci.yaml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v3 - uses: software-mansion/setup-scarb@v1 with: - scarb-version: "nightly-2024-06-01" + scarb-version: "nightly-2024-08-31" - run: scarb fmt --check - run: scarb test diff --git a/stwo_cairo_verifier/.tool-versions b/stwo_cairo_verifier/.tool-versions index aed7d7d52..d7cab96d3 100644 --- a/stwo_cairo_verifier/.tool-versions +++ b/stwo_cairo_verifier/.tool-versions @@ -1 +1,2 @@ -scarb nightly-2024-06-15 +scarb nightly-2024-08-31 +starknet-foundry 0.30.0 diff --git a/stwo_cairo_verifier/Scarb.toml b/stwo_cairo_verifier/Scarb.toml index 53c84a927..ab88f3ec2 100644 --- a/stwo_cairo_verifier/Scarb.toml +++ b/stwo_cairo_verifier/Scarb.toml @@ -1,8 +1,12 @@ [package] name = "stwo_cairo_verifier" version = "0.1.0" -edition = "2023_11" +edition = "2024_07" -# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html +[lib] +casm = true [dependencies] + +[dev-dependencies] +cairo_test = "2.8.0" diff --git a/stwo_cairo_verifier/src/circle.cairo b/stwo_cairo_verifier/src/circle.cairo index df2a9b183..d29bf1ce6 100644 --- a/stwo_cairo_verifier/src/circle.cairo +++ b/stwo_cairo_verifier/src/circle.cairo @@ -32,7 +32,7 @@ pub const CIRCLE_ORDER_BIT_MASK: u32 = 0x7fffffff; pub const U32_BIT_MASK: u64 = 0xffffffff; /// A point on the complex circle. Treated as an additive group. -#[derive(Drop, Copy, Debug, PartialEq, Eq)] +#[derive(Drop, Copy, Debug, PartialEq)] pub struct CirclePoint { pub x: F, pub y: F @@ -106,7 +106,7 @@ pub impl ComplexConjugateImpl of ComplexConjugateTrait { } /// Represents the coset `initial + `. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Drop)] +#[derive(Copy, Clone, Debug, PartialEq, Drop)] pub struct Coset { // This is an index in the range [0, 2^31) pub initial_index: usize, diff --git a/stwo_cairo_verifier/src/fields/cm31.cairo b/stwo_cairo_verifier/src/fields/cm31.cairo index 0f17441d9..3363d3a04 100644 --- a/stwo_cairo_verifier/src/fields/cm31.cairo +++ b/stwo_cairo_verifier/src/fields/cm31.cairo @@ -1,7 +1,7 @@ use core::num::traits::{One, Zero}; use super::m31::{M31, m31, M31Trait}; -#[derive(Copy, Drop, Debug, PartialEq, Eq)] +#[derive(Copy, Drop, Debug, PartialEq)] pub struct CM31 { pub a: M31, pub b: M31, diff --git a/stwo_cairo_verifier/src/fields/m31.cairo b/stwo_cairo_verifier/src/fields/m31.cairo index 7258bf4af..69491fe5c 100644 --- a/stwo_cairo_verifier/src/fields/m31.cairo +++ b/stwo_cairo_verifier/src/fields/m31.cairo @@ -6,7 +6,7 @@ pub const P: u32 = 0x7fffffff; const P32NZ: NonZero = 0x7fffffff; const P64NZ: NonZero = 0x7fffffff; -#[derive(Copy, Drop, Debug, PartialEq, Eq)] +#[derive(Copy, Drop, Debug, PartialEq)] pub struct M31 { pub inner: u32 } diff --git a/stwo_cairo_verifier/src/fields/qm31.cairo b/stwo_cairo_verifier/src/fields/qm31.cairo index d1ab5fd10..f8ba678aa 100644 --- a/stwo_cairo_verifier/src/fields/qm31.cairo +++ b/stwo_cairo_verifier/src/fields/qm31.cairo @@ -5,7 +5,7 @@ use core::num::traits::one::One; pub const R: CM31 = CM31 { a: M31 { inner: 2 }, b: M31 { inner: 1 } }; -#[derive(Copy, Drop, Debug, PartialEq, Eq)] +#[derive(Copy, Drop, Debug, PartialEq)] pub struct QM31 { pub a: CM31, pub b: CM31, diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo index 8a59533a6..23fe23b89 100644 --- a/stwo_cairo_verifier/src/fri.cairo +++ b/stwo_cairo_verifier/src/fri.cairo @@ -1,3 +1,4 @@ +use core::dict::Felt252Dict; use stwo_cairo_verifier::fields::m31::M31Trait; use stwo_cairo_verifier::circle::{Coset, CosetImpl}; use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; diff --git a/stwo_cairo_verifier/src/poly/circle.cairo b/stwo_cairo_verifier/src/poly/circle.cairo index d8e0ab5e0..34668fd41 100644 --- a/stwo_cairo_verifier/src/poly/circle.cairo +++ b/stwo_cairo_verifier/src/poly/circle.cairo @@ -15,7 +15,7 @@ use core::num::traits::ops::wrapping::WrappingSub; /// /// Valid domains are a disjoint union of two conjugate cosets: `+-C + `. /// The ordering defined on this domain is `C + iG_n`, and then `-C - iG_n`. -#[derive(Debug, Copy, Drop, PartialEq, Eq)] +#[derive(Debug, Copy, Drop, PartialEq)] pub struct CircleDomain { pub half_coset: Coset } @@ -61,7 +61,7 @@ pub impl CircleDomainImpl of CircleDomainTrait { /// An evaluation defined on a [CircleDomain]. /// /// The values are ordered according to the [CircleDomain] ordering. -#[derive(Debug, Drop, Clone, PartialEq, Eq)] +#[derive(Debug, Drop, Clone, PartialEq)] pub struct CircleEvaluation { pub values: Array, pub domain: CircleDomain, diff --git a/stwo_cairo_verifier/src/queries.cairo b/stwo_cairo_verifier/src/queries.cairo index 6748a068a..279b701f5 100644 --- a/stwo_cairo_verifier/src/queries.cairo +++ b/stwo_cairo_verifier/src/queries.cairo @@ -9,7 +9,7 @@ use stwo_cairo_verifier::sort::MinimumToMaximumSortedIterator; /// An ordered set of query indices over a bit reversed [CircleDomain]. -#[derive(Drop, Clone, Debug, PartialEq, Eq)] +#[derive(Drop, Clone, Debug, PartialEq)] pub struct Queries { pub positions: Array, pub log_domain_size: u32, diff --git a/stwo_cairo_verifier/src/utils.cairo b/stwo_cairo_verifier/src/utils.cairo index 190c8cbf9..3b5e32f31 100644 --- a/stwo_cairo_verifier/src/utils.cairo +++ b/stwo_cairo_verifier/src/utils.cairo @@ -1,4 +1,5 @@ use core::array::SpanTrait; +use core::dict::Felt252Dict; use core::traits::PanicDestruct; use core::option::OptionTrait; use core::box::BoxTrait; From 733e01e9efc04ef68117bb01a1ffc39277c3956f Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 14:15:18 -0400 Subject: [PATCH 03/27] Add formatting to scarb.toml (#113) --- stwo_cairo_verifier/Scarb.toml | 3 ++ stwo_cairo_verifier/src/channel.cairo | 6 +-- stwo_cairo_verifier/src/circle.cairo | 21 ++++----- stwo_cairo_verifier/src/fields.cairo | 2 +- stwo_cairo_verifier/src/fields/cm31.cairo | 2 +- stwo_cairo_verifier/src/fields/m31.cairo | 8 ++-- stwo_cairo_verifier/src/fields/qm31.cairo | 10 ++--- stwo_cairo_verifier/src/fri.cairo | 51 ++++++++++------------ stwo_cairo_verifier/src/lib.cairo | 6 +-- stwo_cairo_verifier/src/poly/circle.cairo | 19 ++++---- stwo_cairo_verifier/src/poly/line.cairo | 18 +++----- stwo_cairo_verifier/src/queries.cairo | 11 ++--- stwo_cairo_verifier/src/sort.cairo | 2 +- stwo_cairo_verifier/src/utils.cairo | 10 ++--- stwo_cairo_verifier/src/vcs/hasher.cairo | 4 +- stwo_cairo_verifier/src/vcs/verifier.cairo | 12 ++--- 16 files changed, 84 insertions(+), 101 deletions(-) diff --git a/stwo_cairo_verifier/Scarb.toml b/stwo_cairo_verifier/Scarb.toml index ab88f3ec2..204f4d8e5 100644 --- a/stwo_cairo_verifier/Scarb.toml +++ b/stwo_cairo_verifier/Scarb.toml @@ -6,6 +6,9 @@ edition = "2024_07" [lib] casm = true +[tool.fmt] +sort-module-level-items = true + [dependencies] [dev-dependencies] diff --git a/stwo_cairo_verifier/src/channel.cairo b/stwo_cairo_verifier/src/channel.cairo index 8a0987700..4fb25ed9b 100644 --- a/stwo_cairo_verifier/src/channel.cairo +++ b/stwo_cairo_verifier/src/channel.cairo @@ -1,11 +1,11 @@ use core::array::SpanTrait; use core::poseidon::{poseidon_hash_span, hades_permutation}; use core::traits::DivRem; - -use stwo_cairo_verifier::{BaseField, SecureField}; use stwo_cairo_verifier::fields::qm31::QM31Trait; use stwo_cairo_verifier::utils::pack4; +use stwo_cairo_verifier::{BaseField, SecureField}; + const M31_SHIFT: felt252 = 0x80000000; // 2**31. const M31_SHIFT_NZ_U256: NonZero = 0x80000000; // 2**31. pub const EXTENSION_FELTS_PER_HASH: usize = 2; @@ -155,8 +155,8 @@ fn extract_m31(ref num: u256) -> BaseField { #[cfg(test)] mod tests { - use super::{Channel, ChannelTrait}; use stwo_cairo_verifier::fields::qm31::qm31; + use super::ChannelTrait; #[test] fn test_initialize_channel() { diff --git a/stwo_cairo_verifier/src/circle.cairo b/stwo_cairo_verifier/src/circle.cairo index d29bf1ce6..7040da724 100644 --- a/stwo_cairo_verifier/src/circle.cairo +++ b/stwo_cairo_verifier/src/circle.cairo @@ -1,9 +1,10 @@ -use stwo_cairo_verifier::fields::m31::{M31, M31Impl}; +use core::num::traits::one::One; +use core::num::traits::zero::Zero; +use core::num::traits::{WrappingAdd, WideMul}; use stwo_cairo_verifier::fields::cm31::CM31; +use stwo_cairo_verifier::fields::m31::{M31, M31Impl}; use stwo_cairo_verifier::fields::qm31::{QM31Impl, QM31, QM31Trait}; use super::utils::pow; -use core::num::traits::zero::Zero; -use core::num::traits::one::One; pub const M31_CIRCLE_GEN: CirclePoint = CirclePoint { x: M31 { inner: 2 }, y: M31 { inner: 1268011823 }, }; @@ -123,10 +124,10 @@ pub impl CosetImpl of CosetTrait { } fn index_at(self: @Coset, index: usize) -> usize { - let index_times_step = (core::integer::u32_wide_mul(*self.step_size, index) & U32_BIT_MASK) + let index_times_step = ((*self.step_size).wide_mul(index) & U32_BIT_MASK) .try_into() .unwrap(); - let result = core::integer::u32_wrapping_add(*self.initial_index, index_times_step); + let result = (*self.initial_index).wrapping_add(index_times_step); result & CIRCLE_ORDER_BIT_MASK } @@ -178,14 +179,14 @@ pub impl CosetImpl of CosetTrait { #[cfg(test)] mod tests { - use super::{M31_CIRCLE_GEN, CIRCLE_ORDER, CirclePoint, CirclePointM31Impl, Coset, CosetImpl}; - use core::option::OptionTrait; use core::array::ArrayTrait; + use core::option::OptionTrait; use core::traits::TryInto; - use super::{CirclePointQM31Impl, QM31_CIRCLE_GEN}; - use stwo_cairo_verifier::fields::m31::{m31, M31}; - use stwo_cairo_verifier::fields::qm31::{qm31, QM31, QM31One}; + use stwo_cairo_verifier::fields::m31::m31; + use stwo_cairo_verifier::fields::qm31::QM31One; use stwo_cairo_verifier::utils::pow; + use super::{CirclePointQM31Impl, QM31_CIRCLE_GEN}; + use super::{M31_CIRCLE_GEN, CIRCLE_ORDER, CirclePoint, CirclePointM31Impl, Coset, CosetImpl}; #[test] fn test_add_1() { diff --git a/stwo_cairo_verifier/src/fields.cairo b/stwo_cairo_verifier/src/fields.cairo index 8c1737e78..32c0ba186 100644 --- a/stwo_cairo_verifier/src/fields.cairo +++ b/stwo_cairo_verifier/src/fields.cairo @@ -1,5 +1,5 @@ -pub mod m31; pub mod cm31; +pub mod m31; pub mod qm31; pub type BaseField = m31::M31; diff --git a/stwo_cairo_verifier/src/fields/cm31.cairo b/stwo_cairo_verifier/src/fields/cm31.cairo index 3363d3a04..fef2aa656 100644 --- a/stwo_cairo_verifier/src/fields/cm31.cairo +++ b/stwo_cairo_verifier/src/fields/cm31.cairo @@ -71,8 +71,8 @@ pub fn cm31(a: u32, b: u32) -> CM31 { #[cfg(test)] mod tests { - use super::{cm31}; use super::super::m31::{m31, P}; + use super::{cm31}; #[test] fn test_cm31() { diff --git a/stwo_cairo_verifier/src/fields/m31.cairo b/stwo_cairo_verifier/src/fields/m31.cairo index 69491fe5c..b01ba9729 100644 --- a/stwo_cairo_verifier/src/fields/m31.cairo +++ b/stwo_cairo_verifier/src/fields/m31.cairo @@ -1,6 +1,6 @@ +use core::num::traits::{WideMul, CheckedSub}; use core::option::OptionTrait; use core::traits::TryInto; -use core::result::ResultTrait; pub const P: u32 = 0x7fffffff; const P32NZ: NonZero = 0x7fffffff; @@ -45,7 +45,7 @@ pub impl M31Impl of M31Trait { pub impl M31Add of core::traits::Add { fn add(lhs: M31, rhs: M31) -> M31 { let res = lhs.inner + rhs.inner; - let res = core::integer::u32_overflowing_sub(res, P).unwrap_or(res); + let res = res.checked_sub(P).unwrap_or(res); M31 { inner: res } } } @@ -56,7 +56,7 @@ pub impl M31Sub of core::traits::Sub { } pub impl M31Mul of core::traits::Mul { fn mul(lhs: M31, rhs: M31) -> M31 { - M31Impl::reduce_u64(core::integer::u32_wide_mul(lhs.inner, rhs.inner)) + M31Impl::reduce_u64(lhs.inner.wide_mul(rhs.inner)) } } pub impl M31Zero of core::num::traits::Zero { @@ -102,7 +102,7 @@ pub fn m31(val: u32) -> M31 { #[cfg(test)] mod tests { - use super::{m31, P, M31, M31Trait}; + use super::{m31, P, M31Trait}; const POW2_15: u32 = 0b1000000000000000; const POW2_16: u32 = 0b10000000000000000; diff --git a/stwo_cairo_verifier/src/fields/qm31.cairo b/stwo_cairo_verifier/src/fields/qm31.cairo index f8ba678aa..f34dd3ba3 100644 --- a/stwo_cairo_verifier/src/fields/qm31.cairo +++ b/stwo_cairo_verifier/src/fields/qm31.cairo @@ -1,7 +1,7 @@ -use super::m31::{M31, m31, M31Impl}; -use super::cm31::{CM31, cm31, CM31Trait}; -use core::num::traits::zero::Zero; use core::num::traits::one::One; +use core::num::traits::zero::Zero; +use super::cm31::{CM31, cm31, CM31Trait}; +use super::m31::{M31, M31Impl}; pub const R: CM31 = CM31 { a: M31 { inner: 2 }, b: M31 { inner: 1 } }; @@ -113,9 +113,9 @@ pub fn qm31(a: u32, b: u32, c: u32, d: u32) -> QM31 { #[cfg(test)] mod tests { - use super::{QM31, qm31, QM31Trait, QM31Impl}; - use super::{CM31, cm31}; + use super::CM31; use super::super::m31::{m31, P, M31Trait}; + use super::{QM31, qm31, QM31Trait, QM31Impl}; #[test] fn test_QM31() { diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo index 23fe23b89..d2ee8b0c2 100644 --- a/stwo_cairo_verifier/src/fri.cairo +++ b/stwo_cairo_verifier/src/fri.cairo @@ -1,23 +1,23 @@ use core::dict::Felt252Dict; -use stwo_cairo_verifier::fields::m31::M31Trait; -use stwo_cairo_verifier::circle::{Coset, CosetImpl}; -use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; -use stwo_cairo_verifier::fields::qm31::{QM31, qm31, QM31Trait}; -use stwo_cairo_verifier::queries::SparseSubCircleDomain; +use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; +use stwo_cairo_verifier::circle::CosetImpl; use stwo_cairo_verifier::fields::m31::M31; -use stwo_cairo_verifier::utils::{bit_reverse_index, pow, pow_qm31, qm31_zero_array, find}; -use stwo_cairo_verifier::poly::line::{ - LineEvaluation, LineEvaluationImpl, SparseLineEvaluation, SparseLineEvaluationImpl -}; +use stwo_cairo_verifier::fields::m31::M31Trait; +use stwo_cairo_verifier::fields::qm31::{QM31, QM31Trait}; +use stwo_cairo_verifier::poly::circle::CircleDomainImpl; use stwo_cairo_verifier::poly::circle::{ CircleEvaluation, SparseCircleEvaluation, SparseCircleEvaluationImpl }; -use stwo_cairo_verifier::poly::circle::{CircleDomain, CircleDomainImpl}; +use stwo_cairo_verifier::poly::line::{ + LineEvaluation, LineEvaluationImpl, SparseLineEvaluation, SparseLineEvaluationImpl +}; +use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; +use stwo_cairo_verifier::poly::line::{LinePoly, LinePolyImpl}; +use stwo_cairo_verifier::queries::SparseSubCircleDomain; use stwo_cairo_verifier::queries::{Queries, QueriesImpl}; -use stwo_cairo_verifier::vcs::verifier::{MerkleDecommitment, MerkleVerifier, MerkleVerifierTrait}; +use stwo_cairo_verifier::utils::{bit_reverse_index, pow, pow_qm31, qm31_zero_array, find}; use stwo_cairo_verifier::vcs::hasher::PoseidonMerkleHasher; -use stwo_cairo_verifier::poly::line::{LinePoly, LinePolyImpl}; -use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; +use stwo_cairo_verifier::vcs::verifier::{MerkleDecommitment, MerkleVerifier, MerkleVerifierTrait}; pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1; pub const FOLD_STEP: u32 = 1; @@ -531,27 +531,20 @@ pub fn ibutterfly(v0: QM31, v1: QM31, itwid: M31) -> (QM31, QM31) { #[cfg(test)] mod test { - use stwo_cairo_verifier::poly::line::{ - LineEvaluation, SparseLineEvaluation, SparseLineEvaluationImpl - }; - use stwo_cairo_verifier::fields::m31::M31Trait; + use stwo_cairo_verifier::channel::ChannelTrait; use stwo_cairo_verifier::circle::{Coset, CosetImpl}; - use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; - use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; - use stwo_cairo_verifier::fields::m31::M31; - use stwo_cairo_verifier::utils::{bit_reverse_index, pow}; + use stwo_cairo_verifier::fields::qm31::qm31; use stwo_cairo_verifier::poly::circle::{ - CircleEvaluation, CircleEvaluationTrait, CircleDomain, CircleEvaluationImpl, - SparseCircleEvaluation, SparseCircleEvaluationImpl + CircleDomain, CircleEvaluationImpl, SparseCircleEvaluation, SparseCircleEvaluationImpl }; - use stwo_cairo_verifier::queries::{Queries, QueriesImpl}; - use stwo_cairo_verifier::channel::ChannelTrait; + use stwo_cairo_verifier::poly::line::LineDomainImpl; use stwo_cairo_verifier::poly::line::LinePoly; - use stwo_cairo_verifier::vcs::verifier::MerkleDecommitment; - use super::{ - FOLD_STEP, CIRCLE_TO_LINE_FOLD_STEP, FriConfig, FriProof, FriVerifierImpl, - FriVerificationError, FriLayerProof + use stwo_cairo_verifier::poly::line::{ + LineEvaluation, SparseLineEvaluation, SparseLineEvaluationImpl }; + use stwo_cairo_verifier::queries::{Queries, QueriesImpl}; + use stwo_cairo_verifier::vcs::verifier::MerkleDecommitment; + use super::{FriConfig, FriProof, FriVerifierImpl, FriVerificationError, FriLayerProof}; #[test] fn test_fold_line_1() { diff --git a/stwo_cairo_verifier/src/lib.cairo b/stwo_cairo_verifier/src/lib.cairo index 9dbe333c2..1f422bbc0 100644 --- a/stwo_cairo_verifier/src/lib.cairo +++ b/stwo_cairo_verifier/src/lib.cairo @@ -1,12 +1,12 @@ mod channel; mod circle; mod fields; +mod fri; mod poly; +mod queries; +mod sort; mod utils; mod vcs; -mod fri; -mod sort; -mod queries; pub use fields::{BaseField, SecureField}; diff --git a/stwo_cairo_verifier/src/poly/circle.cairo b/stwo_cairo_verifier/src/poly/circle.cairo index 34668fd41..65e63d9ff 100644 --- a/stwo_cairo_verifier/src/poly/circle.cairo +++ b/stwo_cairo_verifier/src/poly/circle.cairo @@ -1,15 +1,12 @@ +use core::num::traits::ops::wrapping::WrappingSub; use stwo_cairo_verifier::circle::CirclePointTrait; -use core::option::OptionTrait; -use core::clone::Clone; -use core::result::ResultTrait; -use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; -use stwo_cairo_verifier::fields::m31::{M31, m31}; -use stwo_cairo_verifier::utils::pow; use stwo_cairo_verifier::circle::{ Coset, CosetImpl, CirclePoint, CirclePointM31Impl, M31_CIRCLE_GEN, CIRCLE_ORDER }; +use stwo_cairo_verifier::fields::m31::M31; +use stwo_cairo_verifier::fields::qm31::QM31; use stwo_cairo_verifier::fri::fold_circle_into_line; -use core::num::traits::ops::wrapping::WrappingSub; +use stwo_cairo_verifier::utils::pow; /// A valid domain for circle polynomial interpolation and evaluation. /// @@ -127,13 +124,13 @@ pub impl SparseCircleEvaluationImpl of SparseCircleEvaluationImplTrait { #[cfg(test)] mod tests { - use super::{ - CircleDomain, CircleDomainTrait, CircleEvaluation, CircleEvaluationImpl, - SparseCircleEvaluation, SparseCircleEvaluationImplTrait - }; use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePoint}; use stwo_cairo_verifier::fields::m31::m31; use stwo_cairo_verifier::fields::qm31::qm31; + use super::{ + CircleDomain, CircleDomainTrait, CircleEvaluationImpl, SparseCircleEvaluation, + SparseCircleEvaluationImplTrait + }; #[test] fn test_circle_domain_at_1() { diff --git a/stwo_cairo_verifier/src/poly/line.cairo b/stwo_cairo_verifier/src/poly/line.cairo index 83af45b6d..7f55d511f 100644 --- a/stwo_cairo_verifier/src/poly/line.cairo +++ b/stwo_cairo_verifier/src/poly/line.cairo @@ -1,13 +1,9 @@ -use core::option::OptionTrait; -use core::clone::Clone; -use core::result::ResultTrait; -use stwo_cairo_verifier::poly::utils::fold; -use stwo_cairo_verifier::fields::SecureField; -use stwo_cairo_verifier::fields::m31::{M31, m31, M31Trait}; -use stwo_cairo_verifier::fields::qm31::{QM31, qm31, QM31Zero}; -use stwo_cairo_verifier::utils::pow; use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePointTrait, M31_CIRCLE_GEN}; +use stwo_cairo_verifier::fields::SecureField; +use stwo_cairo_verifier::fields::m31::{M31, m31}; +use stwo_cairo_verifier::fields::qm31::{QM31, QM31Zero}; use stwo_cairo_verifier::fri::fold_line; +use stwo_cairo_verifier::poly::utils::fold; /// A univariate polynomial defined on a [LineDomain]. #[derive(Debug, Drop, Clone)] @@ -138,11 +134,11 @@ pub impl SparseLineEvaluationImpl of SparseLineEvaluationTrait { #[cfg(test)] mod tests { - use super::{LinePoly, LinePolyTrait, LineDomain, LineDomainImpl}; - use stwo_cairo_verifier::fields::qm31::qm31; + use stwo_cairo_verifier::circle::{CosetImpl, CIRCLE_LOG_ORDER}; use stwo_cairo_verifier::fields::m31::m31; - use stwo_cairo_verifier::circle::{Coset, CosetImpl, CIRCLE_LOG_ORDER}; + use stwo_cairo_verifier::fields::qm31::qm31; use stwo_cairo_verifier::utils::pow; + use super::{LinePoly, LinePolyTrait, LineDomainImpl}; #[test] #[should_panic] diff --git a/stwo_cairo_verifier/src/queries.cairo b/stwo_cairo_verifier/src/queries.cairo index 279b701f5..67d64bff9 100644 --- a/stwo_cairo_verifier/src/queries.cairo +++ b/stwo_cairo_verifier/src/queries.cairo @@ -1,11 +1,8 @@ -use super::utils::{pow, bit_reverse_index, find}; -use core::traits::Copy; -use core::nullable::{NullableTrait, match_nullable, FromNullableResult}; -use core::dict::Felt252DictEntryTrait; -use stwo_cairo_verifier::poly::circle::{CircleDomain, CircleDomainImpl}; -use stwo_cairo_verifier::circle::{Coset, CosetImpl}; use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; +use stwo_cairo_verifier::circle::CosetImpl; +use stwo_cairo_verifier::poly::circle::{CircleDomain, CircleDomainImpl}; use stwo_cairo_verifier::sort::MinimumToMaximumSortedIterator; +use super::utils::{pow, bit_reverse_index, find}; /// An ordered set of query indices over a bit reversed [CircleDomain]. @@ -161,8 +158,8 @@ pub impl SparseSubCircleDomainImpl of SparseSubCircleDomainTrait { #[cfg(test)] mod test { + use stwo_cairo_verifier::channel::ChannelTrait; use super::{Queries, QueriesImpl}; - use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; #[test] fn test_fold_1() { diff --git a/stwo_cairo_verifier/src/sort.cairo b/stwo_cairo_verifier/src/sort.cairo index 0e873fc5d..9f4851c80 100644 --- a/stwo_cairo_verifier/src/sort.cairo +++ b/stwo_cairo_verifier/src/sort.cairo @@ -1,5 +1,5 @@ -use core::array::ToSpanTrait; use core::array::ArrayTrait; +use core::array::ToSpanTrait; use core::option::OptionTrait; trait Compare { diff --git a/stwo_cairo_verifier/src/utils.cairo b/stwo_cairo_verifier/src/utils.cairo index 3b5e32f31..ea791d396 100644 --- a/stwo_cairo_verifier/src/utils.cairo +++ b/stwo_cairo_verifier/src/utils.cairo @@ -1,15 +1,13 @@ use core::array::SpanTrait; -use core::dict::Felt252Dict; -use core::traits::PanicDestruct; -use core::option::OptionTrait; use core::box::BoxTrait; +use core::dict::Felt252Dict; use core::dict::Felt252DictEntryTrait; use core::dict::Felt252DictTrait; -use core::iter::Iterator; use core::num::traits::BitSize; -use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; -use stwo_cairo_verifier::BaseField; use core::traits::DivRem; +use core::traits::PanicDestruct; +use stwo_cairo_verifier::BaseField; +use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; #[generate_trait] pub impl DictImpl, +PanicDestruct> of DictTrait { diff --git a/stwo_cairo_verifier/src/vcs/hasher.cairo b/stwo_cairo_verifier/src/vcs/hasher.cairo index 6f5f2f899..3050d81d2 100644 --- a/stwo_cairo_verifier/src/vcs/hasher.cairo +++ b/stwo_cairo_verifier/src/vcs/hasher.cairo @@ -2,8 +2,6 @@ use core::array::ArrayTrait; use core::option::OptionTrait; use core::poseidon::poseidon_hash_span; use stwo_cairo_verifier::BaseField; -use core::hash::HashStateTrait; -use core::poseidon::{hades_permutation, HashState}; // A Merkle node hash is a hash of: // [left_child_hash, right_child_hash], column0_value, column1_value, ... @@ -65,8 +63,8 @@ pub impl PoseidonMerkleHasher of MerkleHasher { #[cfg(test)] mod tests { - use super::PoseidonMerkleHasher; use stwo_cairo_verifier::fields::m31::{m31}; + use super::PoseidonMerkleHasher; #[test] fn test_m31() { diff --git a/stwo_cairo_verifier/src/vcs/verifier.cairo b/stwo_cairo_verifier/src/vcs/verifier.cairo index d252e7932..43cd14770 100644 --- a/stwo_cairo_verifier/src/vcs/verifier.cairo +++ b/stwo_cairo_verifier/src/vcs/verifier.cairo @@ -1,17 +1,17 @@ -use core::dict::Felt252DictTrait; -use core::result::ResultTrait; -use stwo_cairo_verifier::utils::SpanExTrait; -use core::option::OptionTrait; use core::array::ArrayTrait; use core::array::SpanTrait; use core::array::ToSpanTrait; +use core::cmp::min; use core::dict::Felt252Dict; use core::dict::Felt252DictEntryTrait; -use core::nullable::NullableTrait; -use core::cmp::min; +use core::dict::Felt252DictTrait; use core::fmt::{Debug, Formatter, Error}; +use core::nullable::NullableTrait; +use core::option::OptionTrait; +use core::result::ResultTrait; use stwo_cairo_verifier::BaseField; use stwo_cairo_verifier::fields::m31::m31; +use stwo_cairo_verifier::utils::SpanExTrait; use stwo_cairo_verifier::utils::{ArrayExTrait, DictTrait, OptBoxTrait}; use stwo_cairo_verifier::vcs::hasher::MerkleHasher; From 8095ddbc4f4c8ffb2c6c0ee89988e2b788b8e712 Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 14:22:53 -0400 Subject: [PATCH 04/27] Refactor Coset with CirclePointIndex (#114) --- stwo_cairo_verifier/src/circle.cairo | 269 ++++++++++++---------- stwo_cairo_verifier/src/fields/qm31.cairo | 3 + stwo_cairo_verifier/src/fri.cairo | 95 +++++--- stwo_cairo_verifier/src/poly/circle.cairo | 59 +++-- stwo_cairo_verifier/src/poly/line.cairo | 17 +- 5 files changed, 263 insertions(+), 180 deletions(-) diff --git a/stwo_cairo_verifier/src/circle.cairo b/stwo_cairo_verifier/src/circle.cairo index 7040da724..34848c498 100644 --- a/stwo_cairo_verifier/src/circle.cairo +++ b/stwo_cairo_verifier/src/circle.cairo @@ -1,14 +1,24 @@ use core::num::traits::one::One; use core::num::traits::zero::Zero; -use core::num::traits::{WrappingAdd, WideMul}; +use core::num::traits::{WrappingAdd, WrappingSub, WrappingMul}; use stwo_cairo_verifier::fields::cm31::CM31; use stwo_cairo_verifier::fields::m31::{M31, M31Impl}; use stwo_cairo_verifier::fields::qm31::{QM31Impl, QM31, QM31Trait}; use super::utils::pow; +/// A generator for the circle group over [`M31`]. pub const M31_CIRCLE_GEN: CirclePoint = CirclePoint { x: M31 { inner: 2 }, y: M31 { inner: 1268011823 }, }; +pub const M31_CIRCLE_LOG_ORDER: u32 = 31; + +/// Equals `2^31`. +pub const M31_CIRCLE_ORDER: u32 = 2147483648; + +/// Equals `2^31 - 1`. +pub const M31_CIRCLE_ORDER_BIT_MASK: u32 = 0x7fffffff; + +/// A generator for the circle group over [`QM31`]. pub const QM31_CIRCLE_GEN: CirclePoint = CirclePoint { x: QM31 { @@ -21,16 +31,8 @@ pub const QM31_CIRCLE_GEN: CirclePoint = }, }; -pub const CIRCLE_LOG_ORDER: u32 = 31; - -// `CIRCLE_ORDER` equals 2^31 -pub const CIRCLE_ORDER: u32 = 2147483648; - -// `CIRCLE_ORDER_BIT_MASK` equals 2^31 - 1 -pub const CIRCLE_ORDER_BIT_MASK: u32 = 0x7fffffff; - -// `U32_BIT_MASK` equals 2^32 - 1 -pub const U32_BIT_MASK: u64 = 0xffffffff; +/// Order of [`QM31_CIRCLE_GEN`]. +pub const QM31_CIRCLE_ORDER: u128 = 21267647892944572736998860269687930880; /// A point on the complex circle. Treated as an additive group. #[derive(Drop, Copy, Debug, PartialEq)] @@ -71,18 +73,20 @@ pub trait CirclePointTrait< } fn mul( - self: @CirclePoint, ref scalar: u128 + self: @CirclePoint, scalar: u128 ) -> CirclePoint< F > { + // TODO: `mut scalar: u128` doesn't work in trait. + let mut scalar = scalar; let mut result = Self::zero(); let mut cur = *self; - while scalar > 0 { + while scalar != 0 { if scalar & 1 == 1 { result = result + cur; } cur = cur + cur; - scalar = scalar / 2; + scalar /= 2; }; result } @@ -109,44 +113,34 @@ pub impl ComplexConjugateImpl of ComplexConjugateTrait { /// Represents the coset `initial + `. #[derive(Copy, Clone, Debug, PartialEq, Drop)] pub struct Coset { - // This is an index in the range [0, 2^31) - pub initial_index: usize, - pub step_size: usize, + pub initial_index: CirclePointIndex, + pub step_size: CirclePointIndex, pub log_size: u32, } #[generate_trait] pub impl CosetImpl of CosetTrait { - fn new(initial_index: usize, log_size: u32) -> Coset { - assert!(initial_index < CIRCLE_ORDER); - let step_size = pow(2, CIRCLE_LOG_ORDER - log_size); + fn new(initial_index: CirclePointIndex, log_size: u32) -> Coset { + let step_size = CirclePointIndexImpl::subgroup_gen(log_size); Coset { initial_index, step_size, log_size } } - fn index_at(self: @Coset, index: usize) -> usize { - let index_times_step = ((*self.step_size).wide_mul(index) & U32_BIT_MASK) - .try_into() - .unwrap(); - let result = (*self.initial_index).wrapping_add(index_times_step); - result & CIRCLE_ORDER_BIT_MASK + fn index_at(self: @Coset, index: usize) -> CirclePointIndex { + *self.initial_index + self.step_size.mul(index) } fn double(self: @Coset) -> Coset { assert!(*self.log_size > 0); - let double_initial_index = *self.initial_index * 2; - let double_step_size = *self.step_size * 2; - let new_log_size = *self.log_size - 1; - Coset { - initial_index: double_initial_index & CIRCLE_ORDER_BIT_MASK, - step_size: double_step_size & CIRCLE_ORDER_BIT_MASK, - log_size: new_log_size + initial_index: *self.initial_index + *self.initial_index, + step_size: *self.step_size + *self.step_size, + log_size: *self.log_size - 1 } } + #[inline] fn at(self: @Coset, index: usize) -> CirclePoint { - let mut scalar = self.index_at(index).into(); - M31_CIRCLE_GEN.mul(ref scalar) + self.index_at(index).to_point() } /// Returns the size of the coset. @@ -158,8 +152,7 @@ pub impl CosetImpl of CosetTrait { /// /// For example, for `n=8`, we get the point indices `[1,3,5,7,9,11,13,15]`. fn odds(log_size: u32) -> Coset { - let subgroup_generator_index = Self::subgroup_generator_index(log_size); - Self::new(subgroup_generator_index, log_size) + Self::new(CirclePointIndexImpl::subgroup_gen(log_size + 1), log_size) } /// Creates a coset of the form `G_4n + `. @@ -167,26 +160,90 @@ pub impl CosetImpl of CosetTrait { /// For example, for `n=8`, we get the point indices `[1,5,9,13,17,21,25,29]`. /// Its conjugate will be `[3,7,11,15,19,23,27,31]`. fn half_odds(log_size: u32) -> Coset { - Self::new(Self::subgroup_generator_index(log_size + 2), log_size) + Self::new(CirclePointIndexImpl::subgroup_gen(log_size + 2), log_size) + } +} + +/// Integer `i` that represent the circle point `i * M31_CIRCLE_GEN`. +/// +/// Treated as an additive ring modulo `1 << M31_CIRCLE_LOG_ORDER`. +#[derive(Copy, Debug, Drop)] +pub struct CirclePointIndex { + /// The index, stored as an unreduced `u32` for performance reasons. + index: u32, +} + +#[generate_trait] +pub impl CirclePointIndexImpl of CirclePointIndexTrait { + fn new(index: u32) -> CirclePointIndex { + CirclePointIndex { index } + } + + fn zero() -> CirclePointIndex { + CirclePointIndex { index: 0 } + } + + fn generator() -> CirclePointIndex { + CirclePointIndex { index: 1 } + } + + fn reduce(self: @CirclePointIndex) -> CirclePointIndex { + CirclePointIndex { index: *self.index & M31_CIRCLE_ORDER_BIT_MASK } + } + + fn subgroup_gen(log_size: u32) -> CirclePointIndex { + assert!(log_size <= M31_CIRCLE_LOG_ORDER); + CirclePointIndex { index: pow(2, M31_CIRCLE_LOG_ORDER - log_size) } + } + + // TODO(andrew): When associated types are supported, support the Mul. + fn mul(self: @CirclePointIndex, scalar: u32) -> CirclePointIndex { + CirclePointIndex { index: (*self.index).wrapping_mul(scalar) } + } + + fn index(self: @CirclePointIndex) -> u32 { + self.reduce().index + } + + fn to_point(self: @CirclePointIndex) -> CirclePoint { + // No need to call `reduce()`. + M31_CIRCLE_GEN.mul((*self.index).into()) } +} + +impl CirclePointIndexAdd of Add { + #[inline] + fn add(lhs: CirclePointIndex, rhs: CirclePointIndex) -> CirclePointIndex { + CirclePointIndex { index: lhs.index.wrapping_add(rhs.index) } + } +} - fn subgroup_generator_index(log_size: u32) -> u32 { - assert!(log_size <= CIRCLE_LOG_ORDER); - pow(2, CIRCLE_LOG_ORDER - log_size) +impl CirclePointIndexNeg of Neg { + #[inline] + fn neg(a: CirclePointIndex) -> CirclePointIndex { + CirclePointIndex { index: M31_CIRCLE_ORDER.wrapping_sub(a.index) } } } +impl CirclePointIndexPartialEx of PartialEq { + fn eq(lhs: @CirclePointIndex, rhs: @CirclePointIndex) -> bool { + lhs.index() == rhs.index() + } + + fn ne(lhs: @CirclePointIndex, rhs: @CirclePointIndex) -> bool { + lhs.index() != rhs.index() + } +} #[cfg(test)] mod tests { - use core::array::ArrayTrait; - use core::option::OptionTrait; - use core::traits::TryInto; use stwo_cairo_verifier::fields::m31::m31; - use stwo_cairo_verifier::fields::qm31::QM31One; + use stwo_cairo_verifier::fields::qm31::{QM31One, qm31}; use stwo_cairo_verifier::utils::pow; - use super::{CirclePointQM31Impl, QM31_CIRCLE_GEN}; - use super::{M31_CIRCLE_GEN, CIRCLE_ORDER, CirclePoint, CirclePointM31Impl, Coset, CosetImpl}; + use super::{ + M31_CIRCLE_GEN, CirclePointQM31Impl, QM31_CIRCLE_GEN, M31_CIRCLE_ORDER, CirclePoint, + CirclePointM31Impl, CirclePointIndexImpl, Coset, CosetImpl, QM31_CIRCLE_ORDER + }; #[test] fn test_add_1() { @@ -224,8 +281,7 @@ mod tests { #[test] fn test_mul_1() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; - let mut scalar = 5; - let result = point_1.mul(ref scalar); + let result = point_1.mul(5); assert_eq!(result, point_1 + point_1 + point_1 + point_1 + point_1); } @@ -233,8 +289,7 @@ mod tests { #[test] fn test_mul_2() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; - let mut scalar = 8; - let result = point_1.mul(ref scalar); + let result = point_1.mul(8); assert_eq!( result, point_1 + point_1 + point_1 + point_1 + point_1 + point_1 + point_1 + point_1 @@ -244,17 +299,15 @@ mod tests { #[test] fn test_mul_3() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; - let mut scalar = 418776494; - let result = point_1.mul(ref scalar); + let result = point_1.mul(418776494); assert_eq!(result, CirclePoint { x: m31(1987283985), y: m31(1500510905) }); } #[test] fn test_generator_order() { - let half_order = CIRCLE_ORDER / 2; - let mut scalar = half_order.into(); - let mut result = M31_CIRCLE_GEN.mul(ref scalar); + let half_order = M31_CIRCLE_ORDER / 2; + let mut result = M31_CIRCLE_GEN.mul(half_order.into()); // Assert `M31_CIRCLE_GEN^{2^30}` equals `-1`. assert_eq!(result, CirclePoint { x: -m31(1), y: m31(0) }); @@ -262,38 +315,63 @@ mod tests { #[test] fn test_generator() { - let mut scalar = pow(2, 30).try_into().unwrap(); - let mut result = M31_CIRCLE_GEN.mul(ref scalar); + let mut result = M31_CIRCLE_GEN.mul(pow(2, 30).into()); assert_eq!(result, CirclePoint { x: -m31(1), y: m31(0) }); } #[test] fn test_coset_index_at() { - let coset = Coset { initial_index: 16777216, log_size: 5, step_size: 67108864 }; + let coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + log_size: 5, + step_size: CirclePointIndexImpl::new(67108864) + }; let result = coset.index_at(8); - assert_eq!(result, 553648128); + assert_eq!(result, CirclePointIndexImpl::new(553648128)); } #[test] fn test_coset_constructor() { - let result = CosetImpl::new(16777216, 5); + let result = CosetImpl::new(CirclePointIndexImpl::new(16777216), 5); - assert_eq!(result, Coset { initial_index: 16777216, log_size: 5, step_size: 67108864 }); + assert_eq!( + result, + Coset { + initial_index: CirclePointIndexImpl::new(16777216), + log_size: 5, + step_size: CirclePointIndexImpl::new(67108864) + } + ); } #[test] fn test_coset_double() { - let coset = Coset { initial_index: 16777216, step_size: 67108864, log_size: 5 }; + let coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + step_size: CirclePointIndexImpl::new(67108864), + log_size: 5 + }; let result = coset.double(); - assert_eq!(result, Coset { initial_index: 33554432, step_size: 134217728, log_size: 4 }); + assert_eq!( + result, + Coset { + initial_index: CirclePointIndexImpl::new(33554432), + step_size: CirclePointIndexImpl::new(134217728), + log_size: 4 + } + ); } #[test] fn test_coset_at() { - let coset = Coset { initial_index: 16777216, step_size: 67108864, log_size: 5 }; + let coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + step_size: CirclePointIndexImpl::new(67108864), + log_size: 5 + }; let result = coset.at(17); assert_eq!(result, CirclePoint { x: m31(7144319), y: m31(1742797653) }); @@ -301,7 +379,11 @@ mod tests { #[test] fn test_coset_size() { - let coset = Coset { initial_index: 16777216, step_size: 67108864, log_size: 5 }; + let coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + step_size: CirclePointIndexImpl::new(67108864), + log_size: 5 + }; let result = coset.size(); assert_eq!(result, 32); @@ -309,61 +391,10 @@ mod tests { #[test] fn test_qm31_circle_gen() { - let P4: u128 = 21267647892944572736998860269687930881; - - let first_prime = 2; - let last_prime = 368140581013; - let prime_factors: Array<(u128, u32)> = array![ - (first_prime, 33), - (3, 2), - (5, 1), - (7, 1), - (11, 1), - (31, 1), - (151, 1), - (331, 1), - (733, 1), - (1709, 1), - (last_prime, 1), - ]; - - let product = iter_product(first_prime, @prime_factors, last_prime); - - assert_eq!(product, P4 - 1); - assert_eq!( - QM31_CIRCLE_GEN.x * QM31_CIRCLE_GEN.x + QM31_CIRCLE_GEN.y * QM31_CIRCLE_GEN.y, - QM31One::one() + QM31_CIRCLE_GEN.mul(QM31_CIRCLE_ORDER / 2), + CirclePoint { x: -qm31(1, 0, 0, 0), y: qm31(0, 0, 0, 0) } ); - - let mut scalar = P4 - 1; - assert_eq!(QM31_CIRCLE_GEN.mul(ref scalar), CirclePointQM31Impl::zero()); - - let mut i = 0; - while i < prime_factors.len() { - let (p, _) = *prime_factors.at(i); - let mut scalar = (P4 - 1) / p.into(); - assert_ne!(QM31_CIRCLE_GEN.mul(ref scalar), CirclePointQM31Impl::zero()); - - i = i + 1; - } - } - - fn iter_product( - first_prime: u128, prime_factors: @Array<(u128, u32)>, last_prime: u128 - ) -> u128 { - let mut accum_product: u128 = 1; - accum_product = accum_product - * pow(first_prime.try_into().unwrap(), 31).into() - * 4; // * 2^33 - let mut i = 1; - while i < prime_factors.len() - 1 { - let (prime, exponent): (u128, u32) = *prime_factors.at(i); - accum_product = accum_product * pow(prime.try_into().unwrap(), exponent).into(); - i = i + 1; - }; - accum_product = accum_product * last_prime; - accum_product } } diff --git a/stwo_cairo_verifier/src/fields/qm31.cairo b/stwo_cairo_verifier/src/fields/qm31.cairo index f34dd3ba3..a7533769c 100644 --- a/stwo_cairo_verifier/src/fields/qm31.cairo +++ b/stwo_cairo_verifier/src/fields/qm31.cairo @@ -3,6 +3,9 @@ use core::num::traits::zero::Zero; use super::cm31::{CM31, cm31, CM31Trait}; use super::m31::{M31, M31Impl}; +/// Equals `(2^31 - 1)^4`. +pub const P4: u128 = 21267647892944572736998860269687930881; + pub const R: CM31 = CM31 { a: M31 { inner: 2 }, b: M31 { inner: 1 } }; #[derive(Copy, Drop, Debug, PartialEq)] diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo index d2ee8b0c2..8ae733d72 100644 --- a/stwo_cairo_verifier/src/fri.cairo +++ b/stwo_cairo_verifier/src/fri.cairo @@ -241,12 +241,9 @@ pub impl FriVerifierImpl of FriVerifierTrait { let mut inner_layers = array![]; let mut layer_bound = *max_column_bound - CIRCLE_TO_LINE_FOLD_STEP; - let mut layer_domain = LineDomain { - coset: CosetImpl::new( - pow(2, 31 - layer_bound - config.log_blowup_factor - 2), - layer_bound + config.log_blowup_factor - ) - }; + let mut layer_domain = LineDomainImpl::new( + CosetImpl::half_odds(layer_bound + config.log_blowup_factor) + ); let mut layer_index = 0; let mut invalid_fri_layers_number = false; @@ -532,7 +529,7 @@ pub fn ibutterfly(v0: QM31, v1: QM31, itwid: M31) -> (QM31, QM31) { #[cfg(test)] mod test { use stwo_cairo_verifier::channel::ChannelTrait; - use stwo_cairo_verifier::circle::{Coset, CosetImpl}; + use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePointIndexImpl}; use stwo_cairo_verifier::fields::qm31::qm31; use stwo_cairo_verifier::poly::circle::{ CircleDomain, CircleEvaluationImpl, SparseCircleEvaluation, SparseCircleEvaluationImpl @@ -548,7 +545,7 @@ mod test { #[test] fn test_fold_line_1() { - let domain = LineDomainImpl::new(CosetImpl::new(67108864, 1)); + let domain = LineDomainImpl::new(CosetImpl::new(CirclePointIndexImpl::new(67108864), 1)); let values = array![ qm31(440443058, 1252794525, 1129773609, 1309365757), qm31(974589897, 1592795796, 649052897, 863407657) @@ -565,7 +562,7 @@ mod test { #[test] fn test_fold_line_2() { - let domain = LineDomainImpl::new(CosetImpl::new(553648128, 1)); + let domain = LineDomainImpl::new(CosetImpl::new(CirclePointIndexImpl::new(553648128), 1)); let values = array![ qm31(730692421, 1363821003, 2146256633, 106012305), qm31(1387266930, 149259209, 1148988082, 1930518101) @@ -582,7 +579,9 @@ mod test { #[test] fn test_fold_circle_into_line_1() { - let domain = CircleDomain { half_coset: CosetImpl::new(553648128, 0) }; + let domain = CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(553648128), 0) + }; let values = array![qm31(807167738, 0, 0, 0), qm31(1359821401, 0, 0, 0)]; let sparse_circle_evaluation: SparseCircleEvaluation = SparseCircleEvaluation { subcircle_evals: array![CircleEvaluationImpl::new(domain, values)] @@ -856,7 +855,11 @@ mod test { let queries = Queries { positions: array![5], log_domain_size: 4 }; let domain = CircleDomain { - half_coset: Coset { initial_index: 603979776, step_size: 2147483648, log_size: 0 } + half_coset: Coset { + initial_index: CirclePointIndexImpl::new(603979776), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 + } }; let decommitted_values = array![ SparseCircleEvaluation { @@ -902,7 +905,11 @@ mod test { let queries = Queries { positions: array![5], log_domain_size: 4 }; let domain = CircleDomain { - half_coset: Coset { initial_index: 603979776, step_size: 2147483648, log_size: 0 } + half_coset: Coset { + initial_index: CirclePointIndexImpl::new(603979776), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 + } }; let decommitted_values = array![ SparseCircleEvaluation { @@ -981,7 +988,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 545259520, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(545259520), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(1464849549, 0, 0, 0), qm31(35402781, 0, 0, 0),], @@ -1073,7 +1082,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1619001344, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(1619001344), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(83295654, 0, 0, 0), qm31(666640840, 0, 0, 0)] @@ -1081,7 +1092,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1652555776, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(1652555776), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(1598588979, 0, 0, 0), qm31(1615371031, 0, 0, 0)] @@ -1093,7 +1106,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1090519040, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(1090519040), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(985597997, 0, 0, 0), qm31(139496415, 0, 0, 0)] @@ -1101,7 +1116,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1157627904, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(1157627904), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(1718103579, 0, 0, 0), qm31(1537119660, 0, 0, 0)] @@ -1113,7 +1130,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 33554432, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(33554432), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(1645691043, 0, 0, 0), qm31(2009531552, 0, 0, 0)] @@ -1121,7 +1140,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 167772160, step_size: 2147483648, log_size: 0 + initial_index: CirclePointIndexImpl::new(167772160), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0 } }, array![qm31(354887788, 0, 0, 0), qm31(934393698, 0, 0, 0)] @@ -1217,7 +1238,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 209715200, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(209715200), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(1784241578, 0, 0, 0), qm31(714402375, 0, 0, 0),], @@ -1225,7 +1248,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 578813952, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(578813952), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(673384396, 0, 0, 0), qm31(475618425, 0, 0, 0),], @@ -1233,7 +1258,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 981467136, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(981467136), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(315059915, 0, 0, 0), qm31(558088919, 0, 0, 0),], @@ -1245,7 +1272,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 419430400, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(419430400), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(142767236, 0, 0, 0), qm31(537984732, 0, 0, 0),], @@ -1253,7 +1282,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1157627904, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(1157627904), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(1718103579, 0, 0, 0), qm31(1537119660, 0, 0, 0),], @@ -1261,7 +1292,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1962934272, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(1962934272), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(2124636505, 0, 0, 0), qm31(1506525049, 0, 0, 0),], @@ -1273,7 +1306,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 838860800, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(838860800), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(1014591066, 0, 0, 0), qm31(1931899148, 0, 0, 0),], @@ -1281,7 +1316,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 167772160, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(167772160), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(354887788, 0, 0, 0), qm31(934393698, 0, 0, 0),], @@ -1289,7 +1326,9 @@ mod test { CircleEvaluationImpl::new( CircleDomain { half_coset: Coset { - initial_index: 1778384896, step_size: 2147483648, log_size: 0, + initial_index: CirclePointIndexImpl::new(1778384896), + step_size: CirclePointIndexImpl::new(2147483648), + log_size: 0, }, }, array![qm31(509977960, 0, 0, 0), qm31(1887908506, 0, 0, 0),], diff --git a/stwo_cairo_verifier/src/poly/circle.cairo b/stwo_cairo_verifier/src/poly/circle.cairo index 65e63d9ff..de646bc67 100644 --- a/stwo_cairo_verifier/src/poly/circle.cairo +++ b/stwo_cairo_verifier/src/poly/circle.cairo @@ -1,7 +1,5 @@ -use core::num::traits::ops::wrapping::WrappingSub; -use stwo_cairo_verifier::circle::CirclePointTrait; use stwo_cairo_verifier::circle::{ - Coset, CosetImpl, CirclePoint, CirclePointM31Impl, M31_CIRCLE_GEN, CIRCLE_ORDER + Coset, CosetImpl, CirclePoint, CirclePointM31Impl, CirclePointIndex, CirclePointIndexImpl }; use stwo_cairo_verifier::fields::m31::M31; use stwo_cairo_verifier::fields::qm31::QM31; @@ -26,32 +24,29 @@ pub impl CircleDomainImpl of CircleDomainTrait { } /// Returns the size of the domain. + #[inline] fn size(self: @CircleDomain) -> usize { pow(2, self.log_size()) } /// Returns the log size of the domain. + #[inline] fn log_size(self: @CircleDomain) -> usize { *self.half_coset.log_size + 1 } - /// Returns the circle point index of the `i`th domain element. - fn index_at(self: @CircleDomain, index: usize) -> usize { + /// Returns the [`CirclePointIndex`] of the `i`th domain element. + fn index_at(self: @CircleDomain, index: usize) -> CirclePointIndex { if index < self.half_coset.size() { self.half_coset.index_at(index) } else { - CIRCLE_ORDER.wrapping_sub(self.half_coset.index_at(index - self.half_coset.size())) + -self.half_coset.index_at(index - self.half_coset.size()) } } - /// Returns the `i` th domain element. + #[inline] fn at(self: @CircleDomain, index: usize) -> CirclePoint { - let mut scalar = self.index_at(index).into(); - M31_CIRCLE_GEN.mul(ref scalar) - } - - fn new_with_log_size(log_size: u32) -> CircleDomain { - CircleDomain { half_coset: CosetImpl::half_odds(log_size - 1) } + self.index_at(index).to_point() } } @@ -124,7 +119,7 @@ pub impl SparseCircleEvaluationImpl of SparseCircleEvaluationImplTrait { #[cfg(test)] mod tests { - use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePoint}; + use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePoint, CirclePointIndexImpl}; use stwo_cairo_verifier::fields::m31::m31; use stwo_cairo_verifier::fields::qm31::qm31; use super::{ @@ -134,7 +129,11 @@ mod tests { #[test] fn test_circle_domain_at_1() { - let half_coset = Coset { initial_index: 16777216, step_size: 67108864, log_size: 5 }; + let half_coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + step_size: CirclePointIndexImpl::new(67108864), + log_size: 5 + }; let domain = CircleDomain { half_coset }; let index = 17; let result = domain.at(index); @@ -144,7 +143,11 @@ mod tests { #[test] fn test_circle_domain_at_2() { - let half_coset = Coset { initial_index: 16777216, step_size: 67108864, log_size: 5 }; + let half_coset = Coset { + initial_index: CirclePointIndexImpl::new(16777216), + step_size: CirclePointIndexImpl::new(67108864), + log_size: 5 + }; let domain = CircleDomain { half_coset }; let index = 37; let result = domain.at(index); @@ -158,15 +161,21 @@ mod tests { let lhs = SparseCircleEvaluation { subcircle_evals: array![ CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(16777216, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(16777216), 0) + }, array![qm31(28672, 0, 0, 0), qm31(36864, 0, 0, 0)] ), CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(2030043136, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(2030043136), 0) + }, array![qm31(28672, 0, 0, 0), qm31(36864, 0, 0, 0)] ), CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(2097152000, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(2097152000), 0) + }, array![qm31(2147454975, 0, 0, 0), qm31(2147446783, 0, 0, 0)] ), ] @@ -179,21 +188,27 @@ mod tests { SparseCircleEvaluation { subcircle_evals: array![ CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(16777216, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(16777216), 0) + }, array![ qm31(667173716, 1020487722, 1791736788, 1346152946), qm31(1471361534, 84922130, 1076528072, 810417939) ] ), CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(2030043136, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(2030043136), 0) + }, array![ qm31(667173716, 1020487722, 1791736788, 1346152946), qm31(1471361534, 84922130, 1076528072, 810417939) ] ), CircleEvaluationImpl::new( - CircleDomain { half_coset: CosetImpl::new(2097152000, 0) }, + CircleDomain { + half_coset: CosetImpl::new(CirclePointIndexImpl::new(2097152000), 0) + }, array![ qm31(1480309931, 1126995925, 355746859, 801330701), qm31(676122113, 2062561517, 1070955575, 1337065708) diff --git a/stwo_cairo_verifier/src/poly/line.cairo b/stwo_cairo_verifier/src/poly/line.cairo index 7f55d511f..7ea626ab9 100644 --- a/stwo_cairo_verifier/src/poly/line.cairo +++ b/stwo_cairo_verifier/src/poly/line.cairo @@ -1,4 +1,4 @@ -use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePointTrait, M31_CIRCLE_GEN}; +use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePointIndexImpl, CirclePointTrait}; use stwo_cairo_verifier::fields::SecureField; use stwo_cairo_verifier::fields::m31::{M31, m31}; use stwo_cairo_verifier::fields::qm31::{QM31, QM31Zero}; @@ -64,10 +64,8 @@ pub impl LineDomainImpl of LineDomainTrait { // Let our coset be `E = c + ` with `|E| > 2` then: // 1. if `ord(c) <= ord(G)` the coset contains two points at x=0 // 2. if `ord(c) = 2 * ord(G)` then `c` and `-c` are in our coset - let mut scalar = coset.step_size.into(); - let coset_step = M31_CIRCLE_GEN.mul(ref scalar); assert!( - coset.at(0).log_order() >= coset_step.log_order() + 2, + coset.initial_index.to_point().log_order() >= coset.log_size + 2, "coset x-coordinates not unique" ); } @@ -134,19 +132,16 @@ pub impl SparseLineEvaluationImpl of SparseLineEvaluationTrait { #[cfg(test)] mod tests { - use stwo_cairo_verifier::circle::{CosetImpl, CIRCLE_LOG_ORDER}; + use stwo_cairo_verifier::circle::{CosetImpl, CirclePointIndexImpl}; use stwo_cairo_verifier::fields::m31::m31; use stwo_cairo_verifier::fields::qm31::qm31; - use stwo_cairo_verifier::utils::pow; use super::{LinePoly, LinePolyTrait, LineDomainImpl}; #[test] #[should_panic] fn bad_line_domain() { // This coset doesn't have points with unique x-coordinates. - let LOG_SIZE = 2; - let initial_index = pow(2, CIRCLE_LOG_ORDER - (LOG_SIZE + 1)); - let coset = CosetImpl::new(initial_index, LOG_SIZE); + let coset = CosetImpl::odds(2); LineDomainImpl::new(coset); } @@ -154,7 +149,7 @@ mod tests { #[test] fn line_domain_of_size_two_works() { let LOG_SIZE: u32 = 1; - let coset = CosetImpl::new(0, LOG_SIZE); + let coset = CosetImpl::new(CirclePointIndexImpl::new(0), LOG_SIZE); LineDomainImpl::new(coset); } @@ -162,7 +157,7 @@ mod tests { #[test] fn line_domain_of_size_one_works() { let LOG_SIZE: u32 = 0; - let coset = CosetImpl::new(0, LOG_SIZE); + let coset = CosetImpl::new(CirclePointIndexImpl::new(0), LOG_SIZE); LineDomainImpl::new(coset); } From 3948dcad2a54141b0de41d00423ffe9703f3b5f0 Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 14:27:01 -0400 Subject: [PATCH 05/27] Refactor FRI implementation (#115) --- stwo_cairo_verifier/src/channel.cairo | 17 +- stwo_cairo_verifier/src/circle.cairo | 16 +- stwo_cairo_verifier/src/fields/qm31.cairo | 22 +-- stwo_cairo_verifier/src/fri.cairo | 93 +++++----- stwo_cairo_verifier/src/lib.cairo | 1 - stwo_cairo_verifier/src/poly/circle.cairo | 34 ++-- stwo_cairo_verifier/src/poly/line.cairo | 33 ++-- stwo_cairo_verifier/src/queries.cairo | 45 ++--- stwo_cairo_verifier/src/sort.cairo | 196 ---------------------- stwo_cairo_verifier/src/utils.cairo | 88 ++++++++-- 10 files changed, 187 insertions(+), 358 deletions(-) delete mode 100644 stwo_cairo_verifier/src/sort.cairo diff --git a/stwo_cairo_verifier/src/channel.cairo b/stwo_cairo_verifier/src/channel.cairo index 4fb25ed9b..408359b5f 100644 --- a/stwo_cairo_verifier/src/channel.cairo +++ b/stwo_cairo_verifier/src/channel.cairo @@ -129,13 +129,12 @@ pub impl ChannelImpl of ChannelTrait { fn draw_random_bytes(ref self: Channel) -> Array { let mut cur: u256 = self.draw_felt252().into(); let mut bytes = array![]; - let mut i: usize = 0; - while i < 31 { - let (q, r) = DivRem::div_rem(cur, 256); - bytes.append(r.try_into().unwrap()); - cur = q; - i += 1; - }; + for _ in 0_usize + ..31 { + let (q, r) = DivRem::div_rem(cur, 256); + bytes.append(r.try_into().unwrap()); + cur = q; + }; bytes } } @@ -224,9 +223,7 @@ mod tests { let initial_digest = 0; let mut channel = ChannelTrait::new(initial_digest); - let mut n: usize = 10; - while n > 0 { - n -= 1; + for _ in 0_usize..10 { channel.draw_felt(); }; diff --git a/stwo_cairo_verifier/src/circle.cairo b/stwo_cairo_verifier/src/circle.cairo index 34848c498..1027715aa 100644 --- a/stwo_cairo_verifier/src/circle.cairo +++ b/stwo_cairo_verifier/src/circle.cairo @@ -247,16 +247,15 @@ mod tests { #[test] fn test_add_1() { - let i = CirclePoint { x: m31(0), y: m31(1) }; - let result = i + i; - - assert_eq!(result, CirclePoint { x: -m31(1), y: m31(0) }); + let g4 = CirclePoint { x: m31(0), y: m31(1) }; + assert_eq!(g4 + g4, CirclePoint { x: -m31(1), y: m31(0) }); } #[test] fn test_add_2() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; let point_2 = CirclePoint { x: m31(1737427771), y: m31(309481134) }; + let result = point_1 + point_2; assert_eq!(result, CirclePoint { x: m31(1476625263), y: m31(1040927458) }); @@ -273,6 +272,7 @@ mod tests { fn test_zero_2() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; let point_2 = CirclePointM31Impl::zero(); + let result = point_1 + point_2; assert_eq!(result, point_1.clone()); @@ -281,6 +281,7 @@ mod tests { #[test] fn test_mul_1() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; + let result = point_1.mul(5); assert_eq!(result, point_1 + point_1 + point_1 + point_1 + point_1); @@ -289,6 +290,7 @@ mod tests { #[test] fn test_mul_2() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; + let result = point_1.mul(8); assert_eq!( @@ -299,6 +301,7 @@ mod tests { #[test] fn test_mul_3() { let point_1 = CirclePoint { x: m31(750649172), y: m31(1991648574) }; + let result = point_1.mul(418776494); assert_eq!(result, CirclePoint { x: m31(1987283985), y: m31(1500510905) }); @@ -307,6 +310,7 @@ mod tests { #[test] fn test_generator_order() { let half_order = M31_CIRCLE_ORDER / 2; + let mut result = M31_CIRCLE_GEN.mul(half_order.into()); // Assert `M31_CIRCLE_GEN^{2^30}` equals `-1`. @@ -327,6 +331,7 @@ mod tests { log_size: 5, step_size: CirclePointIndexImpl::new(67108864) }; + let result = coset.index_at(8); assert_eq!(result, CirclePointIndexImpl::new(553648128)); @@ -353,6 +358,7 @@ mod tests { step_size: CirclePointIndexImpl::new(67108864), log_size: 5 }; + let result = coset.double(); assert_eq!( @@ -372,6 +378,7 @@ mod tests { step_size: CirclePointIndexImpl::new(67108864), log_size: 5 }; + let result = coset.at(17); assert_eq!(result, CirclePoint { x: m31(7144319), y: m31(1742797653) }); @@ -384,6 +391,7 @@ mod tests { step_size: CirclePointIndexImpl::new(67108864), log_size: 5 }; + let result = coset.size(); assert_eq!(result, 32); diff --git a/stwo_cairo_verifier/src/fields/qm31.cairo b/stwo_cairo_verifier/src/fields/qm31.cairo index a7533769c..f8eed1ff0 100644 --- a/stwo_cairo_verifier/src/fields/qm31.cairo +++ b/stwo_cairo_verifier/src/fields/qm31.cairo @@ -22,19 +22,11 @@ pub impl QM31Impl of QM31Trait { QM31 { a: CM31 { a: a, b: b }, b: CM31 { a: c, b: d } } } - fn from_u32(arr: [u32; 4]) -> QM31 { - let [a, b, c, d] = arr; - let a_mod_p = M31Impl::reduce_u32(a); - let b_mod_p = M31Impl::reduce_u32(b); - let c_mod_p = M31Impl::reduce_u32(c); - let d_mod_p = M31Impl::reduce_u32(d); - - QM31 { a: CM31 { a: a_mod_p, b: b_mod_p }, b: CM31 { a: c_mod_p, b: d_mod_p } } - } #[inline] fn to_array(self: QM31) -> [M31; 4] { [self.a.a, self.a.b, self.b.a, self.b.b] } + fn inverse(self: QM31) -> QM31 { assert_ne!(self, Zero::zero()); let b2 = self.b * self.b; @@ -43,6 +35,7 @@ pub impl QM31Impl of QM31Trait { let denom_inverse = denom.inverse(); QM31 { a: self.a * denom_inverse, b: -self.b * denom_inverse } } + fn mul_m31(self: QM31, multiplier: M31) -> QM31 { QM31 { a: CM31 { a: self.a.a * multiplier, b: self.a.b * multiplier }, @@ -139,15 +132,4 @@ mod tests { assert_eq!(qm1 * m.inverse().into(), qm1 * qm.inverse()); assert_eq!(qm1.mul_m31(m), qm1 * m.into()); } - - #[test] - fn test_qm31_from_u32() { - let arr = [2147483648, 2, 3, 4]; - let felt = QM31Impl::from_u32(arr); - let expected_felt = QM31 { - a: CM31 { a: m31(1), b: m31(2) }, b: CM31 { a: m31(3), b: m31(4) } - }; - - assert_eq!(felt, expected_felt) - } } diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo index 8ae733d72..11fabf298 100644 --- a/stwo_cairo_verifier/src/fri.cairo +++ b/stwo_cairo_verifier/src/fri.cairo @@ -3,7 +3,7 @@ use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; use stwo_cairo_verifier::circle::CosetImpl; use stwo_cairo_verifier::fields::m31::M31; use stwo_cairo_verifier::fields::m31::M31Trait; -use stwo_cairo_verifier::fields::qm31::{QM31, QM31Trait}; +use stwo_cairo_verifier::fields::qm31::{QM31, QM31Zero, QM31Trait}; use stwo_cairo_verifier::poly::circle::CircleDomainImpl; use stwo_cairo_verifier::poly::circle::{ CircleEvaluation, SparseCircleEvaluation, SparseCircleEvaluationImpl @@ -15,7 +15,7 @@ use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; use stwo_cairo_verifier::poly::line::{LinePoly, LinePolyImpl}; use stwo_cairo_verifier::queries::SparseSubCircleDomain; use stwo_cairo_verifier::queries::{Queries, QueriesImpl}; -use stwo_cairo_verifier::utils::{bit_reverse_index, pow, pow_qm31, qm31_zero_array, find}; +use stwo_cairo_verifier::utils::{bit_reverse_index, ArrayImpl, pow, pow_qm31, find}; use stwo_cairo_verifier::vcs::hasher::PoseidonMerkleHasher; use stwo_cairo_verifier::vcs::verifier::{MerkleDecommitment, MerkleVerifier, MerkleVerifierTrait}; @@ -48,7 +48,7 @@ impl FriLayerVerifierImpl of FriLayerVerifierTrait { ) -> Result<(Queries, Array), FriVerificationError> { let commitment = self.proof.commitment; - let sparse_evaluation = @self.extract_evaluation(queries, evals_at_queries)?; + let sparse_evaluation = self.extract_evaluation(queries, evals_at_queries)?; let mut column_0: Array = array![]; let mut column_1: Array = array![]; let mut column_2: Array = array![]; @@ -335,7 +335,7 @@ pub impl FriVerifierImpl of FriVerifierTrait { let circle_poly_alpha_sq = *circle_poly_alpha * *circle_poly_alpha; let mut layer_queries = queries.fold(CIRCLE_TO_LINE_FOLD_STEP); - let mut layer_query_evals = qm31_zero_array(layer_queries.len()); + let mut layer_query_evals = ArrayImpl::new_repeated(layer_queries.len(), QM31Zero::zero()); let mut inner_layers_index = 0; let mut column_bound_index = 0; @@ -398,23 +398,23 @@ pub impl FriVerifierImpl of FriVerifierTrait { fn decommit_last_layer( self: @FriVerifier, queries: Queries, query_evals: Array, ) -> Result<(), FriVerificationError> { - let mut failed = false; + let FriVerifier { last_layer_domain, last_layer_poly, .. } = self; + let mut i = 0; - while i < queries.positions.len() { - let query = queries.positions[i]; - let domain = self.last_layer_domain; - let x = self.last_layer_domain.at(bit_reverse_index(*query, domain.log_size())); + loop { + if i == queries.positions.len() { + break Result::Ok(()); + } - if *query_evals[i] != self.last_layer_poly.eval_at_point(x.into()) { - failed = true; - break; + let query = *queries.positions[i]; + let query_eval = *query_evals[i]; + let x = last_layer_domain.at(bit_reverse_index(query, last_layer_domain.log_size())); + + if query_eval != last_layer_poly.eval_at_point(x.into()) { + break Result::Err(FriVerificationError::LastLayerEvaluationsInvalid); } + i += 1; - }; - if failed { - return Result::Err(FriVerificationError::LastLayerEvaluationsInvalid); - } else { - Result::Ok(()) } } @@ -483,43 +483,41 @@ fn get_opening_positions( /// element and `pi(x) = 2x^2 - 1` be the circle's x-coordinate doubling map. This function /// returns `f' = f0 + alpha * f1` evaluated on `pi(E)` such that `2f(x) = f0(pi(x)) + x * /// f1(pi(x))`. -pub fn fold_line(eval: @LineEvaluation, alpha: QM31) -> LineEvaluation { +pub fn fold_line(eval: LineEvaluation, alpha: QM31) -> LineEvaluation { let domain = eval.domain; let mut values = array![]; - let mut i = 0; - while i < eval.values.len() / 2 { - let x = domain.at(bit_reverse_index(i * pow(2, FOLD_STEP), domain.log_size())); - let f_x = eval.values[2 * i]; - let f_neg_x = eval.values[2 * i + 1]; - let (f0, f1) = ibutterfly(*f_x, *f_neg_x, x.inverse()); - values.append(f0 + alpha * f1); - i += 1; - }; + for i in 0 + ..eval.values.len() + / 2 { + let x = domain.at(bit_reverse_index(i * pow(2, FOLD_STEP), domain.log_size())); + let f_x = eval.values[2 * i]; + let f_neg_x = eval.values[2 * i + 1]; + let (f0, f1) = ibutterfly(*f_x, *f_neg_x, x.inverse()); + values.append(f0 + alpha * f1); + }; LineEvaluationImpl::new(domain.double(), values) } -/// Folds and accumulates a degree `d` circle polynomial into a degree `d/2` univariate -/// polynomial. +/// Folds and accumulates a degree `d` circle polynomial into a degree `d/2` univariate polynomial. /// -/// Let `src` be the evaluation of a circle polynomial `f` on a -/// [`CircleDomain`] `E`. This function computes evaluations of `f' = f0 -/// + alpha * f1` on the x-coordinates of `E` such that `2f(p) = f0(px) + py * f1(px)`. The -/// evaluations of `f'` are accumulated into `dst` by the formula `dst = dst * alpha^2 + -/// f'`. -pub fn fold_circle_into_line(eval: @CircleEvaluation, alpha: QM31) -> LineEvaluation { +/// Let `src` be the evaluation of a circle polynomial `f` on a [`CircleDomain`] `E`. This function +/// computes evaluations of `f' = f0 + alpha * f1` on the x-coordinates of `E` such that `2f(p) = +/// f0(px) + py * f1(px)`. The evaluations of `f'` are accumulated into `dst` by the formula +/// `dst = dst * alpha^2 + f'`. +pub fn fold_circle_into_line(eval: CircleEvaluation, alpha: QM31) -> LineEvaluation { let domain = eval.domain; + let fold_factor = pow(2, CIRCLE_TO_LINE_FOLD_STEP); let mut values = array![]; - let mut i = 0; - while i < eval.values.len() / 2 { - let p = domain - .at(bit_reverse_index(i * pow(2, CIRCLE_TO_LINE_FOLD_STEP), domain.log_size())); - let f_p = eval.values[2 * i]; - let f_neg_p = eval.values[2 * i + 1]; - let (f0, f1) = ibutterfly(*f_p, *f_neg_p, p.y.inverse()); - values.append(f0 + alpha * f1); - i += 1; - }; - LineEvaluation { values, domain: LineDomainImpl::new(*domain.half_coset) } + for i in 0 + ..eval.bit_reversed_values.len() + / 2 { + let p = domain.at(bit_reverse_index(i * fold_factor, domain.log_size())); + let f_p = eval.bit_reversed_values[2 * i]; + let f_neg_p = eval.bit_reversed_values[2 * i + 1]; + let (f0, f1) = ibutterfly(*f_p, *f_neg_p, p.y.inverse()); + values.append(f0 + alpha * f1); + }; + LineEvaluation { values, domain: LineDomainImpl::new(domain.half_coset) } } pub fn ibutterfly(v0: QM31, v1: QM31, itwid: M31) -> (QM31, QM31) { @@ -639,11 +637,10 @@ mod test { #[test] fn proof_with_removed_layer_fails_verification() { let (config, proof, bounds, _queries, _decommitted_values) = proof_with_mixed_degree_1(); - let mut invalid_config = config; invalid_config.log_last_layer_degree_bound -= 1; - let mut channel = ChannelTrait::new(0x00); + let result = FriVerifierImpl::commit(ref channel, invalid_config, proof, bounds); match result { diff --git a/stwo_cairo_verifier/src/lib.cairo b/stwo_cairo_verifier/src/lib.cairo index 1f422bbc0..dda065f03 100644 --- a/stwo_cairo_verifier/src/lib.cairo +++ b/stwo_cairo_verifier/src/lib.cairo @@ -4,7 +4,6 @@ mod fields; mod fri; mod poly; mod queries; -mod sort; mod utils; mod vcs; diff --git a/stwo_cairo_verifier/src/poly/circle.cairo b/stwo_cairo_verifier/src/poly/circle.cairo index de646bc67..f803c44be 100644 --- a/stwo_cairo_verifier/src/poly/circle.cairo +++ b/stwo_cairo_verifier/src/poly/circle.cairo @@ -55,25 +55,21 @@ pub impl CircleDomainImpl of CircleDomainTrait { /// The values are ordered according to the [CircleDomain] ordering. #[derive(Debug, Drop, Clone, PartialEq)] pub struct CircleEvaluation { - pub values: Array, + pub bit_reversed_values: Array, pub domain: CircleDomain, - _eval_order: BitReversedOrder } -#[derive(Debug, Drop, Clone, PartialEq)] -struct BitReversedOrder {} - #[generate_trait] pub impl CircleEvaluationImpl of CircleEvaluationTrait { - fn new(domain: CircleDomain, values: Array) -> CircleEvaluation { - CircleEvaluation { values: values, domain: domain, _eval_order: BitReversedOrder {} } + fn new(domain: CircleDomain, bit_reversed_values: Array) -> CircleEvaluation { + CircleEvaluation { bit_reversed_values, domain } } } /// Holds a foldable subset of circle polynomial evaluations. #[derive(Drop, Clone, Debug, PartialEq)] pub struct SparseCircleEvaluation { - pub subcircle_evals: Array + pub subcircle_evals: Array, } #[generate_trait] @@ -92,10 +88,10 @@ pub impl SparseCircleEvaluationImpl of SparseCircleEvaluationImplTrait { let lhs = self.subcircle_evals[i]; let rhs = rhs.subcircle_evals[i]; let mut values = array![]; - assert_eq!(lhs.values.len(), rhs.values.len()); + assert_eq!(lhs.bit_reversed_values.len(), rhs.bit_reversed_values.len()); let mut j = 0; - while j < lhs.values.len() { - values.append(*lhs.values[j] * alpha + *rhs.values[j]); + while j < lhs.bit_reversed_values.len() { + values.append(*lhs.bit_reversed_values[j] * alpha + *rhs.bit_reversed_values[j]); j += 1; }; subcircle_evals.append(CircleEvaluationImpl::new(*lhs.domain, values)); @@ -105,15 +101,13 @@ pub impl SparseCircleEvaluationImpl of SparseCircleEvaluationImplTrait { SparseCircleEvaluation { subcircle_evals } } - fn fold(self: @SparseCircleEvaluation, alpha: QM31) -> Array { - let mut i = 0; - let mut res: Array = array![]; - while i < self.subcircle_evals.len() { - let circle_evaluation = fold_circle_into_line(self.subcircle_evals[i], alpha); - res.append(*circle_evaluation.values.at(0)); - i += 1; - }; - return res; + fn fold(self: SparseCircleEvaluation, alpha: QM31) -> Array { + let mut res = array![]; + for eval in self + .subcircle_evals { + res.append(*fold_circle_into_line(eval, alpha).values[0]) + }; + res } } diff --git a/stwo_cairo_verifier/src/poly/line.cairo b/stwo_cairo_verifier/src/poly/line.cairo index 7ea626ab9..1f0bef4d1 100644 --- a/stwo_cairo_verifier/src/poly/line.cairo +++ b/stwo_cairo_verifier/src/poly/line.cairo @@ -117,13 +117,10 @@ pub struct SparseLineEvaluation { #[generate_trait] pub impl SparseLineEvaluationImpl of SparseLineEvaluationTrait { - fn fold(self: @SparseLineEvaluation, alpha: QM31) -> Array { - let mut i = 0; - let mut res: Array = array![]; - while i < self.subline_evals.len() { - let line_evaluation = fold_line(self.subline_evals[i], alpha); - res.append(*line_evaluation.values.at(0)); - i += 1; + fn fold(self: SparseLineEvaluation, alpha: QM31) -> Array { + let mut res = array![]; + for eval in self.subline_evals { + res.append(*fold_line(eval, alpha).values[0]); }; res } @@ -142,23 +139,18 @@ mod tests { fn bad_line_domain() { // This coset doesn't have points with unique x-coordinates. let coset = CosetImpl::odds(2); - LineDomainImpl::new(coset); } #[test] fn line_domain_of_size_two_works() { - let LOG_SIZE: u32 = 1; - let coset = CosetImpl::new(CirclePointIndexImpl::new(0), LOG_SIZE); - + let coset = CosetImpl::new(CirclePointIndexImpl::new(0), 1); LineDomainImpl::new(coset); } #[test] fn line_domain_of_size_one_works() { - let LOG_SIZE: u32 = 0; - let coset = CosetImpl::new(CirclePointIndexImpl::new(0), LOG_SIZE); - + let coset = CosetImpl::new(CirclePointIndexImpl::new(0), 0); LineDomainImpl::new(coset); } @@ -172,9 +164,10 @@ mod tests { log_size: 1 }; let x = m31(590768354); + let result = line_poly.eval_at_point(x.into()); - let expected_result = qm31(515899232, 1030391528, 1006544539, 11142505); - assert_eq!(expected_result, result); + + assert_eq!(result, qm31(515899232, 1030391528, 1006544539, 11142505)); } #[test] @@ -183,9 +176,10 @@ mod tests { coeffs: array![qm31(1, 2, 3, 4), qm31(5, 6, 7, 8)], log_size: 1 }; let x = m31(10); + let result = line_poly.eval_at_point(x.into()); - let expected_result = qm31(51, 62, 73, 84); - assert_eq!(expected_result, result); + + assert_eq!(result, qm31(51, 62, 73, 84)); } #[test] @@ -207,7 +201,6 @@ mod tests { let result = poly.eval_at_point(x); - let expected_result = qm31(1857853974, 839310133, 939318020, 651207981); - assert_eq!(expected_result, result); + assert_eq!(result, qm31(1857853974, 839310133, 939318020, 651207981)); } } diff --git a/stwo_cairo_verifier/src/queries.cairo b/stwo_cairo_verifier/src/queries.cairo index 67d64bff9..56560cd21 100644 --- a/stwo_cairo_verifier/src/queries.cairo +++ b/stwo_cairo_verifier/src/queries.cairo @@ -1,8 +1,7 @@ use stwo_cairo_verifier::channel::{Channel, ChannelTrait}; use stwo_cairo_verifier::circle::CosetImpl; use stwo_cairo_verifier::poly::circle::{CircleDomain, CircleDomainImpl}; -use stwo_cairo_verifier::sort::MinimumToMaximumSortedIterator; -use super::utils::{pow, bit_reverse_index, find}; +use super::utils::{pow, bit_reverse_index, find, ArrayImpl}; /// An ordered set of query indices over a bit reversed [CircleDomain]. @@ -16,7 +15,7 @@ pub struct Queries { pub impl QueriesImpl of QueriesImplTrait { /// Randomizes a set of query indices uniformly over the range [0, 2^`log_query_size`). fn generate(ref channel: Channel, log_domain_size: u32, n_queries: usize) -> Queries { - let mut nonsorted_positions = array![]; + let mut unsorted_positions = array![]; let max_query = pow(2, log_domain_size) - 1; let mut finished = false; loop { @@ -27,9 +26,9 @@ pub impl QueriesImpl of QueriesImplTrait { let b1: u32 = (*random_bytes[i + 1]).into(); let b2: u32 = (*random_bytes[i + 2]).into(); let b3: u32 = (*random_bytes[i + 3]).into(); - nonsorted_positions - .append((b0 + 256 * b1 + 65536 * b2 + 16777216 * b3) & max_query); - if nonsorted_positions.len() == n_queries { + let position = (((b3 * 0x100 + b2) * 0x100 + b1) * 0x100 + b0) & max_query; + unsorted_positions.append(position); + if unsorted_positions.len() == n_queries { finished = true; break; } @@ -40,13 +39,7 @@ pub impl QueriesImpl of QueriesImplTrait { } }; - let mut positions = array![]; - let mut iterator = MinimumToMaximumSortedIterator::iterate(nonsorted_positions.span()); - while let Option::Some((_, x)) = iterator.next_deduplicated() { - positions.append(x); - }; - - Queries { positions, log_domain_size } + Queries { positions: unsorted_positions.sort_ascending().dedup(), log_domain_size } } fn len(self: @Queries) -> usize { @@ -98,7 +91,7 @@ pub impl QueriesImpl of QueriesImplTrait { } } -/// Represents a circle domain relative to a larger circle domain. The `initial_index` is the bit +/// Represents a circle domain relative to a larger circle domain. The `coset_index` is the bit /// reversed query index in the larger domain. #[derive(Drop, Debug, Copy)] pub struct SubCircleDomain { @@ -111,13 +104,12 @@ pub struct SubCircleDomain { pub impl SubCircleDomainImpl of SubCircleDomainTrait { /// Calculates the decommitment positions needed for each query given the fri step size. fn to_decommitment_positions(self: @SubCircleDomain) -> Array { + let sub_circle_size = pow(2, *self.log_size); + let start = *self.coset_index * sub_circle_size; + let end = start + sub_circle_size; let mut res = array![]; - let start = *self.coset_index * pow(2, *self.log_size); - let end = (*self.coset_index + 1) * pow(2, *self.log_size); - let mut i = start; - while i < end { + for i in start..end { res.append(i); - i = i + 1; }; res } @@ -142,16 +134,13 @@ pub struct SparseSubCircleDomain { pub impl SparseSubCircleDomainImpl of SparseSubCircleDomainTrait { fn flatten(self: @SparseSubCircleDomain) -> Array { let mut res = array![]; - let mut i = 0; - while i < self.domains.len() { - let positions = self.domains[i].to_decommitment_positions(); - let mut j = 0; - while j < positions.len() { - res.append(*positions[j]); - j = j + 1; + for domain in self + .domains + .span() { + for position in domain.to_decommitment_positions() { + res.append(position); + }; }; - i = i + 1; - }; res } } diff --git a/stwo_cairo_verifier/src/sort.cairo b/stwo_cairo_verifier/src/sort.cairo deleted file mode 100644 index 9f4851c80..000000000 --- a/stwo_cairo_verifier/src/sort.cairo +++ /dev/null @@ -1,196 +0,0 @@ -use core::array::ArrayTrait; -use core::array::ToSpanTrait; -use core::option::OptionTrait; - -trait Compare { - fn compare(self: @C, a: T, b: T) -> bool; -} - -#[derive(Drop, Copy)] -pub struct LowerThan {} - -impl LowerThanCompare> of Compare { - fn compare(self: @LowerThan, a: T, b: T) -> bool { - return a < b; - } -} - -#[derive(Drop, Copy)] -pub struct GreaterThan {} - -impl GreaterThanCompare, +Copy, +Drop> of Compare { - fn compare(self: @GreaterThan, a: T, b: T) -> bool { - return a > b; - } -} - -#[derive(Drop)] -pub struct SortedIterator { - comparer: C, - array: Span, - last_index: Option, -} - -trait SortedIteratorTrait< - T, C, +PartialOrd, +PartialEq, +Copy, +Drop, +Compare, +Drop, +Copy -> { - fn iterate(array_to_iterate: Span) -> SortedIterator; - - fn next_deduplicated( - ref self: SortedIterator - ) -> Option<(u32, T)> { - next_deduplicated::(ref self) - } - - fn next( - ref self: SortedIterator - ) -> Option< - (u32, T) - > { - if self.last_index.is_some() { - let last_index = self.last_index.unwrap(); - let last_value = *self.array[last_index]; - let mut is_repeated = false; - - let mut i = last_index + 1; - while i < self.array.len() { - if *self.array[i] == last_value { - is_repeated = true; - self.last_index = Option::Some(i); - break; - } - i += 1; - }; - - if is_repeated { - return Option::Some((self.last_index.unwrap(), last_value)); - } - } - next_deduplicated::(ref self) - } -} - -fn next_deduplicated< - T, C, +PartialOrd, +PartialEq, +Copy, +Drop, +Compare, +Drop, +Copy ->( - ref self: SortedIterator -) -> Option<(u32, T)> { - let mut candidate_index = Option::None; - let mut candidate_value = Option::None; - - let last_value = if let Option::Some(last_index) = self.last_index { - Option::Some(*self.array[last_index]) - } else { - Option::None - }; - - let mut i = 0; - while i < self.array.len() { - let is_better_than_last = if let Option::Some(last_value) = last_value { - self.comparer.compare(last_value, *self.array[i]) - } else { - true - }; - let is_nearer_than_candidate = if let Option::Some(candidate_value) = candidate_value { - self.comparer.compare(*self.array[i], candidate_value) - } else { - true - }; - if is_better_than_last && is_nearer_than_candidate { - candidate_index = Option::Some(i); - candidate_value = Option::Some(*self.array[i]); - } - i += 1; - }; - - if candidate_value.is_none() { - Option::None - } else { - self.last_index = candidate_index; - Option::Some((candidate_index.unwrap(), candidate_value.unwrap())) - } -} - -pub impl MaximumToMinimumSortedIterator< - T, +PartialOrd, +PartialEq, +Copy, +Drop -> of SortedIteratorTrait { - fn iterate(array_to_iterate: Span) -> SortedIterator { - SortedIterator { - comparer: GreaterThan {}, array: array_to_iterate, last_index: Option::None - } - } -} - -pub impl MinimumToMaximumSortedIterator< - T, +PartialOrd, +PartialEq, +Copy, +Drop -> of SortedIteratorTrait { - fn iterate(array_to_iterate: Span) -> SortedIterator { - SortedIterator { comparer: LowerThan {}, array: array_to_iterate, last_index: Option::None } - } -} - - -#[test] -fn test_sort_lowest_to_greatest() { - let my_array: Array = array![3, 5, 2, 4]; - let expected_array: Array = array![2, 3, 4, 5]; - - let mut sorted_array = array![]; - - let mut iterator = MinimumToMaximumSortedIterator::iterate(my_array.span()); - while let Option::Some((_index, value)) = iterator.next_deduplicated() { - sorted_array.append(value); - }; - - assert_eq!(expected_array, sorted_array); -} - -#[test] -fn test_sort_greatest_to_lowest() { - let my_array: Array = array![3, 5, 2, 4]; - let expected_array: Array = array![5, 4, 3, 2]; - - let mut sorted_array = array![]; - - let mut iterator = MaximumToMinimumSortedIterator::iterate(my_array.span()); - while let Option::Some((_index, value)) = iterator.next_deduplicated() { - sorted_array.append(value); - }; - - assert_eq!(expected_array, sorted_array); -} - -#[test] -fn test_sort_indexes_are_correct() { - let my_array: Array = array![3, 5, 2, 4]; - let expected_indexes: Array = array![2, 0, 3, 1]; - - let mut sorted_indexes = array![]; - - let mut iterator = MinimumToMaximumSortedIterator::iterate(my_array.span()); - while let Option::Some((index, _value)) = iterator.next_deduplicated() { - sorted_indexes.append(index); - }; - - assert_eq!(expected_indexes, sorted_indexes); -} - -#[test] -fn test_sort_with_duplicates() { - let my_array: Array = array![3, 5, 2, 3, 4, 3, 4]; - let expected_indexes: Array = array![2, 0, 3, 5, 4, 6, 1]; - let expected_array: Array = array![2, 3, 3, 3, 4, 4, 5]; - - let mut sorted_indexes = array![]; - let mut sorted_array = array![]; - - let mut iterator = MinimumToMaximumSortedIterator::iterate(my_array.span()); - while let Option::Some((index, value)) = iterator.next() { - sorted_array.append(value); - sorted_indexes.append(index); - }; - - assert_eq!(expected_indexes, sorted_indexes); - assert_eq!(expected_array, sorted_array); -} - diff --git a/stwo_cairo_verifier/src/utils.cairo b/stwo_cairo_verifier/src/utils.cairo index ea791d396..400910e06 100644 --- a/stwo_cairo_verifier/src/utils.cairo +++ b/stwo_cairo_verifier/src/utils.cairo @@ -3,6 +3,8 @@ use core::box::BoxTrait; use core::dict::Felt252Dict; use core::dict::Felt252DictEntryTrait; use core::dict::Felt252DictTrait; +use core::iter::IntoIterator; +use core::iter::Iterator; use core::num::traits::BitSize; use core::traits::DivRem; use core::traits::PanicDestruct; @@ -42,9 +44,68 @@ pub impl ArrayImpl, +Drop> of ArrayExTrait { }; res } + fn max<+PartialOrd>(mut self: @Array) -> Option<@T> { self.span().max() } + + /// Sorts an array in ascending order. Uses quicksort algorithm. + fn sort_ascending<+PartialOrd>(self: Array) -> Array { + if self.len() <= 1 { + return self; + } + + let mut lhs = array![]; + let mut rhs = array![]; + let mut iter = self.into_iter(); + let pivot = iter.next().unwrap(); + + for v in iter { + if v > pivot { + rhs.append(v); + } else { + lhs.append(v); + } + }; + + let mut res = lhs.sort_ascending(); + res.append(pivot); + + for v in rhs.sort_ascending() { + res.append(v); + }; + + res + } + + /// Removes consecutive repeated elements. + /// + /// If the vector is sorted, this removes all duplicates. + fn dedup<+PartialEq>(self: Array) -> Array { + if self.len() == 0 { + return array![]; + } + + let mut iter = self.into_iter(); + let mut last_value = iter.next().unwrap(); + let mut res = array![last_value]; + for value in iter { + if value != last_value { + res.append(value); + last_value = value; + } + }; + + res + } + + fn new_repeated(n: usize, v: T) -> Array { + let mut res = array![]; + for _ in 0..n { + res.append(v); + }; + res + } } #[generate_trait] @@ -142,19 +203,9 @@ pub fn pow_qm31(base: QM31, mut exponent: u32) -> QM31 { result } -pub fn qm31_zero_array(n: u32) -> Array { - let mut result = array![]; - let mut i = 0; - while i < n { - result.append(qm31(0, 0, 0, 0)); - i += 1; - }; - result -} - #[cfg(test)] mod tests { - use super::{pow, pow_qm31, qm31, bit_reverse_index}; + use super::{pow, pow_qm31, qm31, bit_reverse_index, ArrayImpl}; #[test] fn test_pow() { @@ -210,5 +261,20 @@ mod tests { let expected_result = qm31(1394542587, 260510989, 997191897, 2127074080); assert_eq!(expected_result, result) } + + #[test] + fn test_sort_ascending() { + assert_eq!(array![6_usize, 5, 1, 4, 2, 3].sort_ascending(), array![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn test_dedup() { + assert_eq!(array![1_usize, 1, 1, 2, 2, 3, 4, 5, 5, 5].dedup(), array![1, 2, 3, 4, 5]); + } + + #[test] + fn test_array_new_repeated() { + assert_eq!(ArrayImpl::new_repeated(5, 3_usize), array![3, 3, 3, 3, 3]); + } } From 8f2bf7f262e55ae17384eaf39d76b06f99b30ce4 Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 18:10:49 -0400 Subject: [PATCH 06/27] Add unreduced field arithmetic (#116) --- stwo_cairo_verifier/src/fields/cm31.cairo | 84 +++++- stwo_cairo_verifier/src/fields/m31.cairo | 92 ++++++ stwo_cairo_verifier/src/fields/qm31.cairo | 336 +++++++++++++++++++++- 3 files changed, 508 insertions(+), 4 deletions(-) diff --git a/stwo_cairo_verifier/src/fields/cm31.cairo b/stwo_cairo_verifier/src/fields/cm31.cairo index fef2aa656..0daef42fd 100644 --- a/stwo_cairo_verifier/src/fields/cm31.cairo +++ b/stwo_cairo_verifier/src/fields/cm31.cairo @@ -1,5 +1,6 @@ use core::num::traits::{One, Zero}; -use super::m31::{M31, m31, M31Trait}; +use core::ops::{AddAssign, MulAssign, SubAssign}; +use super::m31::{M31, M31Impl, m31, M31Trait}; #[derive(Copy, Drop, Debug, PartialEq)] pub struct CM31 { @@ -14,34 +15,104 @@ pub impl CM31Impl of CM31Trait { let denom_inverse: M31 = (self.a * self.a + self.b * self.b).inverse(); CM31 { a: self.a * denom_inverse, b: -self.b * denom_inverse } } + + /// Computes all `1/arr[i]` with a single call to `inverse()` using Montgomery batch inversion. + fn batch_inverse(arr: Array) -> Array { + // Construct array `1, z, zy, ..., zy..b`. + let mut prefix_product_rev = array![]; + let mut cumulative_product: CM31 = One::one(); + + let mut i = arr.len(); + while i != 0 { + i -= 1; + prefix_product_rev.append(cumulative_product); + cumulative_product *= *arr[i]; + }; + + // Compute `1/zy..a`. + let mut cumulative_product_inv = cumulative_product.inverse(); + // Compute all `1/a = zy..b/zy..a, 1/b = zy..c/zy..b, ...`. + let mut inverses = array![]; + + let mut i = prefix_product_rev.len(); + for v in arr { + i -= 1; + inverses.append(cumulative_product_inv * *prefix_product_rev[i]); + cumulative_product_inv *= v; + }; + + inverses + } + + // TODO(andrew): When associated types are supported, support `Mul`. + #[inline] + fn mul_m31(self: CM31, rhs: M31) -> CM31 { + CM31 { a: self.a * rhs, b: self.b * rhs } + } + + // TODO(andrew): When associated types are supported, support `Sub`. + #[inline] + fn sub_m31(self: CM31, rhs: M31) -> CM31 { + CM31 { a: self.a - rhs, b: self.b } + } } pub impl CM31Add of core::traits::Add { + #[inline] fn add(lhs: CM31, rhs: CM31) -> CM31 { CM31 { a: lhs.a + rhs.a, b: lhs.b + rhs.b } } } + pub impl CM31Sub of core::traits::Sub { + #[inline] fn sub(lhs: CM31, rhs: CM31) -> CM31 { CM31 { a: lhs.a - rhs.a, b: lhs.b - rhs.b } } } + pub impl CM31Mul of core::traits::Mul { + #[inline] fn mul(lhs: CM31, rhs: CM31) -> CM31 { CM31 { a: lhs.a * rhs.a - lhs.b * rhs.b, b: lhs.a * rhs.b + lhs.b * rhs.a } } } + +pub impl CM31AddAssign of AddAssign { + #[inline] + fn add_assign(ref self: CM31, rhs: CM31) { + self = self + rhs + } +} + +pub impl CM31SubAssign of SubAssign { + #[inline] + fn sub_assign(ref self: CM31, rhs: CM31) { + self = self - rhs + } +} + +pub impl CM31MulAssign of MulAssign { + #[inline] + fn mul_assign(ref self: CM31, rhs: CM31) { + self = self * rhs + } +} + pub impl CM31Zero of Zero { fn zero() -> CM31 { cm31(0, 0) } + fn is_zero(self: @CM31) -> bool { (*self).a.is_zero() && (*self).b.is_zero() } + fn is_non_zero(self: @CM31) -> bool { (*self).a.is_non_zero() || (*self).b.is_non_zero() } } + pub impl CM31One of One { fn one() -> CM31 { cm31(1, 0) @@ -53,17 +124,28 @@ pub impl CM31One of One { (*self).a.is_non_one() || (*self).b.is_non_zero() } } + pub impl M31IntoCM31 of core::traits::Into { + #[inline] fn into(self: M31) -> CM31 { CM31 { a: self, b: m31(0) } } } + pub impl CM31Neg of Neg { + #[inline] fn neg(a: CM31) -> CM31 { CM31 { a: -a.a, b: -a.b } } } +impl CM31PartialOrd of PartialOrd { + fn lt(lhs: CM31, rhs: CM31) -> bool { + lhs.a < rhs.a || (lhs.a == rhs.a && lhs.b < rhs.b) + } +} + +#[inline] pub fn cm31(a: u32, b: u32) -> CM31 { CM31 { a: m31(a), b: m31(b) } } diff --git a/stwo_cairo_verifier/src/fields/m31.cairo b/stwo_cairo_verifier/src/fields/m31.cairo index b01ba9729..a3f819c51 100644 --- a/stwo_cairo_verifier/src/fields/m31.cairo +++ b/stwo_cairo_verifier/src/fields/m31.cairo @@ -1,11 +1,20 @@ use core::num::traits::{WideMul, CheckedSub}; +use core::ops::{AddAssign, MulAssign, SubAssign}; use core::option::OptionTrait; use core::traits::TryInto; +/// Equals `2^31 - 1`. pub const P: u32 = 0x7fffffff; + +/// Equals `2^31 - 1`. const P32NZ: NonZero = 0x7fffffff; + +/// Equals `2^31 - 1`. const P64NZ: NonZero = 0x7fffffff; +/// Equals `2^31 - 1`. +const P128NZ: NonZero = 0x7fffffff; + #[derive(Copy, Drop, Debug, PartialEq)] pub struct M31 { pub inner: u32 @@ -13,16 +22,24 @@ pub struct M31 { #[generate_trait] pub impl M31Impl of M31Trait { + #[inline] fn reduce_u32(val: u32) -> M31 { let (_, res) = core::integer::u32_safe_divmod(val, P32NZ); M31 { inner: res.try_into().unwrap() } } + #[inline] fn reduce_u64(val: u64) -> M31 { let (_, res) = core::integer::u64_safe_divmod(val, P64NZ); M31 { inner: res.try_into().unwrap() } } + #[inline] + fn reduce_u128(val: u128) -> M31 { + let (_, res) = core::integer::u128_safe_divmod(val, P128NZ); + M31 { inner: res.try_into().unwrap() } + } + #[inline] fn sqn(v: M31, n: usize) -> M31 { if n == 0 { @@ -43,45 +60,81 @@ pub impl M31Impl of M31Trait { } } pub impl M31Add of core::traits::Add { + #[inline] fn add(lhs: M31, rhs: M31) -> M31 { let res = lhs.inner + rhs.inner; let res = res.checked_sub(P).unwrap_or(res); M31 { inner: res } } } + pub impl M31Sub of core::traits::Sub { + #[inline] fn sub(lhs: M31, rhs: M31) -> M31 { lhs + (-rhs) } } + pub impl M31Mul of core::traits::Mul { + #[inline] fn mul(lhs: M31, rhs: M31) -> M31 { M31Impl::reduce_u64(lhs.inner.wide_mul(rhs.inner)) } } + +pub impl M31AddAssign of AddAssign { + #[inline] + fn add_assign(ref self: M31, rhs: M31) { + self = self + rhs + } +} + +pub impl M31SubAssign of SubAssign { + #[inline] + fn sub_assign(ref self: M31, rhs: M31) { + self = self - rhs + } +} + +pub impl M31MulAssign of MulAssign { + #[inline] + fn mul_assign(ref self: M31, rhs: M31) { + self = self * rhs + } +} + pub impl M31Zero of core::num::traits::Zero { + #[inline] fn zero() -> M31 { M31 { inner: 0 } } + fn is_zero(self: @M31) -> bool { *self.inner == 0 } + fn is_non_zero(self: @M31) -> bool { *self.inner != 0 } } + pub impl M31One of core::num::traits::One { + #[inline] fn one() -> M31 { M31 { inner: 1 } } + fn is_one(self: @M31) -> bool { *self.inner == 1 } + fn is_non_one(self: @M31) -> bool { *self.inner != 1 } } + pub impl M31Neg of Neg { + #[inline] fn neg(a: M31) -> M31 { if a.inner == 0 { M31 { inner: 0 } @@ -90,16 +143,55 @@ pub impl M31Neg of Neg { } } } + impl M31IntoFelt252 of Into { + #[inline] fn into(self: M31) -> felt252 { self.inner.into() } } +impl M31PartialOrd of PartialOrd { + fn ge(lhs: M31, rhs: M31) -> bool { + lhs.inner >= rhs.inner + } + + fn lt(lhs: M31, rhs: M31) -> bool { + lhs.inner < rhs.inner + } +} + +#[inline] pub fn m31(val: u32) -> M31 { M31Impl::reduce_u32(val) } +#[derive(Copy, Drop, Debug)] +pub struct UnreducedM31 { + pub inner: felt252, +} + +pub impl UnreducedM31Sub of Sub { + #[inline] + fn sub(lhs: UnreducedM31, rhs: UnreducedM31) -> UnreducedM31 { + UnreducedM31 { inner: lhs.inner - rhs.inner } + } +} + +pub impl UnreducedM31Add of Add { + #[inline] + fn add(lhs: UnreducedM31, rhs: UnreducedM31) -> UnreducedM31 { + UnreducedM31 { inner: lhs.inner + rhs.inner } + } +} + +impl M31IntoUnreducedM31 of Into { + #[inline] + fn into(self: M31) -> UnreducedM31 { + UnreducedM31 { inner: self.inner.into() } + } +} + #[cfg(test)] mod tests { use super::{m31, P, M31Trait}; diff --git a/stwo_cairo_verifier/src/fields/qm31.cairo b/stwo_cairo_verifier/src/fields/qm31.cairo index f8eed1ff0..284f4d8c3 100644 --- a/stwo_cairo_verifier/src/fields/qm31.cairo +++ b/stwo_cairo_verifier/src/fields/qm31.cairo @@ -1,7 +1,8 @@ use core::num::traits::one::One; use core::num::traits::zero::Zero; +use core::ops::{AddAssign, MulAssign, SubAssign}; use super::cm31::{CM31, cm31, CM31Trait}; -use super::m31::{M31, M31Impl}; +use super::m31::{M31, M31Impl, UnreducedM31}; /// Equals `(2^31 - 1)^4`. pub const P4: u128 = 21267647892944572736998860269687930881; @@ -36,6 +37,7 @@ pub impl QM31Impl of QM31Trait { QM31 { a: self.a * denom_inverse, b: -self.b * denom_inverse } } + #[inline] fn mul_m31(self: QM31, multiplier: M31) -> QM31 { QM31 { a: CM31 { a: self.a.a * multiplier, b: self.a.b * multiplier }, @@ -43,75 +45,370 @@ pub impl QM31Impl of QM31Trait { } } + // TODO(andrew): When associated types are supported, support `Mul`. + #[inline] + fn mul_cm31(self: QM31, rhs: CM31) -> QM31 { + QM31 { a: self.a * rhs, b: self.b * rhs } + } + fn complex_conjugate(self: QM31) -> QM31 { QM31 { a: self.a, b: -self.b } } + + /// Returns a fused multiply-subtract i.e. returns `a * b - c`. + #[inline] + fn fms(a: QM31, b: QM31, c: QM31) -> QM31 { + (Self::mul_unreduced(a, b) - c.into()).reduce() + } + + /// Returns a fused multiply-add i.e. returns `a * b + c`. + #[inline] + fn fma(a: QM31, b: QM31, c: QM31) -> QM31 { + (Self::mul_unreduced(a, b) + c.into()).reduce() + } + + /// Returns `lhs * rhs` in unreduced form. + /// + /// Output coordinates are returned in the range `[P * P, P * P * 13)`. + // TODO(andrew): May be net worse performance doing unreduced arithmetic due to all felt252 + // multiplications (which are expensive for the M31 prover to simulate). Measure overall + // prove+verify performance differences with unreduced felt252 vs reduced u32. If prover + // performance is an issue consider Karatsuba. + #[inline] + fn mul_unreduced(lhs: QM31, rhs: QM31) -> UnreducedQM31 { + /// Equals `P * P * 16`. + const PP16: felt252 = 0x3fffffff000000010; + + // `lhs` 1st CM31 coordinate. + let lhs_aa: felt252 = lhs.a.a.into(); + let lhs_ab: felt252 = lhs.a.b.into(); + + // `lhs` 2nd CM31 coordinate. + let lhs_ba: felt252 = lhs.b.a.into(); + let lhs_bb: felt252 = lhs.b.b.into(); + + // `rhs` 1st CM31 coordinate. + let rhs_aa: felt252 = rhs.a.a.into(); + let rhs_ab: felt252 = rhs.a.b.into(); + + // `rhs` 2nd CM31 coordinate. + let rhs_ba: felt252 = rhs.b.a.into(); + let rhs_bb: felt252 = rhs.b.b.into(); + + // lhs.a * rhs.a + let (aa_t_ba_a, aa_t_ba_b) = { + let res_a = lhs_aa * rhs_aa - lhs_ab * rhs_ab; + let res_b = lhs_aa * rhs_ab + lhs_ab * rhs_aa; + (res_a, res_b) + }; + + // R * lhs.b * rhs.b + let (r_t_ab_t_bb_a, r_t_ab_t_bb_b) = { + let res_a = lhs_ba * rhs_ba - lhs_bb * rhs_bb; + let res_b = lhs_ba * rhs_bb + lhs_bb * rhs_ba; + (res_a + res_a - res_b, res_a + res_b + res_b) + }; + + // lhs.a * rhs.b + let (aa_t_bb_a, aa_t_bb_b) = { + let res_a = lhs_aa * rhs_ba - lhs_ab * rhs_bb; + let res_b = lhs_aa * rhs_bb + lhs_ab * rhs_ba; + (res_a, res_b) + }; + + // lhs.b * rhs.a + let (ab_t_ba_a, ab_t_ba_b) = { + let res_a = lhs_ba * rhs_aa - lhs_bb * rhs_ab; + let res_b = lhs_ba * rhs_ab + lhs_bb * rhs_aa; + (res_a, res_b) + }; + + UnreducedQM31 { + a: PP16 + aa_t_ba_a + r_t_ab_t_bb_a, + b: PP16 + aa_t_ba_b + r_t_ab_t_bb_b, + c: PP16 + aa_t_bb_a + ab_t_ba_a, + d: PP16 + aa_t_bb_b + ab_t_ba_b + } + } } pub impl QM31Add of core::traits::Add { + #[inline] fn add(lhs: QM31, rhs: QM31) -> QM31 { QM31 { a: lhs.a + rhs.a, b: lhs.b + rhs.b } } } + pub impl QM31Sub of core::traits::Sub { fn sub(lhs: QM31, rhs: QM31) -> QM31 { QM31 { a: lhs.a - rhs.a, b: lhs.b - rhs.b } } } + pub impl QM31Mul of core::traits::Mul { + #[inline] fn mul(lhs: QM31, rhs: QM31) -> QM31 { // (a + bu) * (c + du) = (ac + rbd) + (ad + bc)u. QM31 { a: lhs.a * rhs.a + R * lhs.b * rhs.b, b: lhs.a * rhs.b + lhs.b * rhs.a } } } + +pub impl QM31AddAssign of AddAssign { + #[inline] + fn add_assign(ref self: QM31, rhs: QM31) { + self = self + rhs + } +} + +pub impl QM31SubAssign of SubAssign { + #[inline] + fn sub_assign(ref self: QM31, rhs: QM31) { + self = self - rhs + } +} + +pub impl QM31MulAssign of MulAssign { + #[inline] + fn mul_assign(ref self: QM31, rhs: QM31) { + self = self * rhs + } +} + pub impl QM31Zero of Zero { + #[inline] fn zero() -> QM31 { QM31 { a: Zero::zero(), b: Zero::zero() } } + fn is_zero(self: @QM31) -> bool { (*self).a.is_zero() && (*self).b.is_zero() } + fn is_non_zero(self: @QM31) -> bool { (*self).a.is_non_zero() || (*self).b.is_non_zero() } } + pub impl QM31One of One { + #[inline] fn one() -> QM31 { QM31 { a: One::one(), b: Zero::zero() } } + fn is_one(self: @QM31) -> bool { (*self).a.is_one() && (*self).b.is_zero() } + fn is_non_one(self: @QM31) -> bool { (*self).a.is_non_one() || (*self).b.is_non_zero() } } + pub impl M31IntoQM31 of core::traits::Into { + #[inline] fn into(self: M31) -> QM31 { QM31 { a: self.into(), b: Zero::zero() } } } + pub impl CM31IntoQM31 of core::traits::Into { + #[inline] fn into(self: CM31) -> QM31 { QM31 { a: self, b: Zero::zero() } } } + pub impl QM31Neg of Neg { + #[inline] fn neg(a: QM31) -> QM31 { QM31 { a: -a.a, b: -a.b } } } +impl QM31PartialOrd of PartialOrd { + #[inline] + fn lt(lhs: QM31, rhs: QM31) -> bool { + lhs.a < rhs.a || (lhs.a == rhs.a && lhs.b < rhs.b) + } +} + +#[inline] pub fn qm31(a: u32, b: u32, c: u32, d: u32) -> QM31 { QM31 { a: cm31(a, b), b: cm31(c, d) } } +/// Stores an unreduced [`QM31`] with each coordinate stored as a `felt252`. +#[derive(Copy, Drop, Debug)] +struct UnreducedQM31 { + a: felt252, + b: felt252, + c: felt252, + d: felt252, +} + +#[generate_trait] +impl UnreducedQM31Impl of UnreducedQM31Trait { + #[inline] + fn reduce(self: UnreducedQM31) -> QM31 { + QM31 { + a: CM31 { + a: M31Impl::reduce_u128(self.a.try_into().unwrap()), + b: M31Impl::reduce_u128(self.b.try_into().unwrap()) + }, + b: CM31 { + a: M31Impl::reduce_u128(self.c.try_into().unwrap()), + b: M31Impl::reduce_u128(self.d.try_into().unwrap()) + }, + } + } +} + +impl UnreducedQM31Sub of Sub { + #[inline] + fn sub(lhs: UnreducedQM31, rhs: UnreducedQM31) -> UnreducedQM31 { + UnreducedQM31 { a: lhs.a - rhs.a, b: lhs.b - rhs.b, c: lhs.c - rhs.c, d: lhs.d - rhs.d } + } +} + +impl UnreducedQM31Add of Add { + #[inline] + fn add(lhs: UnreducedQM31, rhs: UnreducedQM31) -> UnreducedQM31 { + UnreducedQM31 { a: lhs.a + rhs.a, b: lhs.b + rhs.b, c: lhs.c + rhs.c, d: lhs.d + rhs.d } + } +} + +impl QM31IntoUnreducedQM31 of Into { + #[inline] + fn into(self: QM31) -> UnreducedQM31 { + UnreducedQM31 { + a: self.a.a.inner.into(), + b: self.a.b.inner.into(), + c: self.b.a.inner.into(), + d: self.b.b.inner.into() + } + } +} + +/// Stores an unreduced [`QM31`] packed into two `felt252`. +/// +/// Is more efficient than [`UnreducedQM31`] since only requires two felt252 operations per addition +/// or M31 multiplication vs 4. +// TODO: Determine if performance difference between UnreducedQM31 and PackedUnreducedQM31 is worth +// keeping around both types. +#[derive(Copy, Drop, Debug)] +pub struct PackedUnreducedQM31 { + pub a: PackedUnreducedCM31, + pub b: PackedUnreducedCM31, +} + +#[generate_trait] +pub impl PackedUnreducedQM31Impl of PackedUnreducedQM31Trait { + #[inline] + fn mul_m31(self: PackedUnreducedQM31, rhs: UnreducedM31) -> PackedUnreducedQM31 { + PackedUnreducedQM31 { a: self.a.mul_m31(rhs), b: self.b.mul_m31(rhs) } + } + + /// Returns a zero element with each coordinate set to `P*P*P`. + #[inline] + fn large_zero() -> PackedUnreducedQM31 { + PackedUnreducedQM31 { + a: PackedUnreducedCM31Impl::large_zero(), b: PackedUnreducedCM31Impl::large_zero(), + } + } + + #[inline] + fn reduce(self: PackedUnreducedQM31) -> QM31 { + QM31 { a: self.a.reduce(), b: self.b.reduce() } + } +} + +pub impl PackedUnreducedQM31AddAssign of AddAssign { + #[inline] + fn add_assign(ref self: PackedUnreducedQM31, rhs: PackedUnreducedQM31) { + self = self + rhs + } +} + +pub impl PackedUnreducedQM31Add of Add { + #[inline] + fn add(lhs: PackedUnreducedQM31, rhs: PackedUnreducedQM31) -> PackedUnreducedQM31 { + PackedUnreducedQM31 { a: lhs.a + rhs.a, b: lhs.b + rhs.b } + } +} + +pub impl PackedUnreducedQM31Sub of Sub { + #[inline] + fn sub(lhs: PackedUnreducedQM31, rhs: PackedUnreducedQM31) -> PackedUnreducedQM31 { + PackedUnreducedQM31 { a: lhs.a - rhs.a, b: lhs.b - rhs.b } + } +} + +pub impl QM31IntoPackedUnreducedQM31 of Into { + #[inline] + fn into(self: QM31) -> PackedUnreducedQM31 { + PackedUnreducedQM31 { a: self.a.into(), b: self.b.into() } + } +} + +/// An unreduced [`CM31`] packed into a single `felt252`. +#[derive(Copy, Drop, Debug)] +pub struct PackedUnreducedCM31 { + /// Stores a 128 bit and 124 bit unreduced M31 packed into a felt252 i.e. `a + (b << 128)`. + pub inner: felt252, +} + +#[generate_trait] +pub impl PackedUnreducedCM31Impl of PackedUnreducedCM31Trait { + #[inline] + fn mul_m31(self: PackedUnreducedCM31, rhs: UnreducedM31) -> PackedUnreducedCM31 { + PackedUnreducedCM31 { inner: self.inner * rhs.inner } + } + + /// Returns a zero element with each coordinate set to `P*P*P`. + #[inline] + fn large_zero() -> PackedUnreducedCM31 { + // Stores `P*P*P + (P*P*P << 128)`. + const PPP_PPP: felt252 = 0x1fffffff400000017fffffff000000001fffffff400000017fffffff; + PackedUnreducedCM31 { inner: PPP_PPP } + } + + #[inline] + fn reduce(self: PackedUnreducedCM31) -> CM31 { + let u256 { low: a, high: b } = self.inner.into(); + CM31 { a: M31Impl::reduce_u128(a), b: M31Impl::reduce_u128(b) } + } +} + +pub impl PackedUnreducedCM31Add of Add { + #[inline] + fn add(lhs: PackedUnreducedCM31, rhs: PackedUnreducedCM31) -> PackedUnreducedCM31 { + PackedUnreducedCM31 { inner: lhs.inner + rhs.inner } + } +} + +pub impl PackedUnreducedCM31Sub of Sub { + #[inline] + fn sub(lhs: PackedUnreducedCM31, rhs: PackedUnreducedCM31) -> PackedUnreducedCM31 { + PackedUnreducedCM31 { inner: lhs.inner - rhs.inner } + } +} + +pub impl CM31IntoPackedUnreducedCM31 of Into { + #[inline] + fn into(self: CM31) -> PackedUnreducedCM31 { + const POW2_128: felt252 = 0x100000000000000000000000000000000; + let a_felt: felt252 = self.a.into(); + let b_felt: felt252 = self.b.into(); + PackedUnreducedCM31 { inner: a_felt + b_felt * POW2_128 } + } +} #[cfg(test)] mod tests { - use super::CM31; use super::super::m31::{m31, P, M31Trait}; - use super::{QM31, qm31, QM31Trait, QM31Impl}; + use super::{ + QM31, qm31, QM31Trait, QM31Impl, QM31IntoPackedUnreducedQM31, PackedUnreducedQM31Impl + }; #[test] fn test_QM31() { @@ -132,4 +429,37 @@ mod tests { assert_eq!(qm1 * m.inverse().into(), qm1 * qm.inverse()); assert_eq!(qm1.mul_m31(m), qm1 * m.into()); } + + #[test] + fn test_fma() { + let a = qm31(P - 4, P - 90, 958, P - 1); + let b = qm31(P - 183, 75, P - 921, P - 6124); + let c = qm31(2, P - 1, P - 1, P - 8); + + let res = QM31Impl::fma(a, b, c); + + assert_eq!(res, a * b + c); + } + + #[test] + fn test_fms() { + let a = qm31(P - 4, P - 90, 958, P - 1); + let b = qm31(P - 183, 75, P - 921, P - 6124); + let c = qm31(2, P - 1, P - 1, P - 8); + + let res = QM31Impl::fms(a, b, c); + + assert_eq!(res, a * b - c); + } + + #[test] + fn test_packed_unreduced_qm31() { + let a = qm31(P - 4, P - 90, 958, P - 1); + let b = m31(P - 183); + + let res_unreduced = QM31IntoPackedUnreducedQM31::into(a).mul_m31(b.into()); + let res = res_unreduced.reduce(); + + assert_eq!(res, a.mul_m31(b)); + } } From c609d09e378f0ffd4a647a66626928ede8f4da9e Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 18:13:18 -0400 Subject: [PATCH 07/27] Add CanonicCoset (#117) --- stwo_cairo_verifier/src/poly/circle.cairo | 53 ++++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/stwo_cairo_verifier/src/poly/circle.cairo b/stwo_cairo_verifier/src/poly/circle.cairo index f803c44be..1018abd46 100644 --- a/stwo_cairo_verifier/src/poly/circle.cairo +++ b/stwo_cairo_verifier/src/poly/circle.cairo @@ -50,9 +50,58 @@ pub impl CircleDomainImpl of CircleDomainTrait { } } -/// An evaluation defined on a [CircleDomain]. +/// A coset of the form `G_{2n} + `, where `G_n` is the generator of the subgroup of order `n`. /// -/// The values are ordered according to the [CircleDomain] ordering. +/// The ordering on this coset is `G_{2n} + i * G_n`. These cosets can be used as a +/// [`CircleDomain`], and be interpolated on. Note that this changes the ordering on the coset to be +/// like [`CircleDomain`], which is `G_{2n} + i * G_{n/2}` and then `-G_{2n} -i * G_{n/2}`. For +/// example, the `X`s below are a canonic coset with `n = 8`. +/// +/// ```text +/// X O X +/// O O +/// X X +/// O O +/// X X +/// O O +/// X O X +/// ``` +#[derive(Copy, Debug, PartialEq, Drop)] +pub struct CanonicCoset { + pub coset: Coset, +} + +#[generate_trait] +pub impl CanonicCosetImpl of CanonicCosetTrait { + fn new(log_size: u32) -> CanonicCoset { + assert!(log_size > 0); + CanonicCoset { coset: CosetImpl::odds(log_size), } + } + + /// Gets the full coset represented `G_{2n} + `. + fn coset(self: @CanonicCoset) -> @Coset { + self.coset + } + + /// Gets half of the coset (its conjugate complements to the whole coset), `G_{2n} + `. + fn half_coset(self: @CanonicCoset) -> Coset { + assert!(*self.coset.log_size > 0); + Coset { + initial_index: *self.coset.initial_index, + step_size: *self.coset.step_size + *self.coset.step_size, + log_size: *self.coset.log_size - 1, + } + } + + /// Gets the [`CircleDomain`] representing the same point set (in another order). + fn circle_domain(self: @CanonicCoset) -> CircleDomain { + CircleDomainImpl::new(self.half_coset()) + } +} + +/// An evaluation defined on a [`CircleDomain`]. +/// +/// The values are ordered according to the [`CircleDomain`] ordering. #[derive(Debug, Drop, Clone, PartialEq)] pub struct CircleEvaluation { pub bit_reversed_values: Array, From ecdb398842e167f450f6410a25413eef1aeef6b5 Mon Sep 17 00:00:00 2001 From: Andrew Milson Date: Sun, 20 Oct 2024 18:16:23 -0400 Subject: [PATCH 08/27] Compute FRI quotients (#118) --- stwo_cairo_verifier/src/circle.cairo | 6 + stwo_cairo_verifier/src/lib.cairo | 15 + stwo_cairo_verifier/src/pcs.cairo | 1 + stwo_cairo_verifier/src/pcs/quotients.cairo | 826 ++++++++++++++++++++ stwo_cairo_verifier/src/queries.cairo | 13 + 5 files changed, 861 insertions(+) create mode 100644 stwo_cairo_verifier/src/pcs.cairo create mode 100644 stwo_cairo_verifier/src/pcs/quotients.cairo diff --git a/stwo_cairo_verifier/src/circle.cairo b/stwo_cairo_verifier/src/circle.cairo index 1027715aa..e386054d1 100644 --- a/stwo_cairo_verifier/src/circle.cairo +++ b/stwo_cairo_verifier/src/circle.cairo @@ -103,6 +103,12 @@ pub impl CirclePointM31Impl of CirclePointTrait {} pub impl CirclePointQM31Impl of CirclePointTrait {} +impl CirclePointQM31PartialOrd of PartialOrd> { + fn lt(lhs: CirclePoint, rhs: CirclePoint) -> bool { + lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y) + } +} + #[generate_trait] pub impl ComplexConjugateImpl of ComplexConjugateTrait { fn complex_conjugate(self: CirclePoint) -> CirclePoint { diff --git a/stwo_cairo_verifier/src/lib.cairo b/stwo_cairo_verifier/src/lib.cairo index dda065f03..1747098b0 100644 --- a/stwo_cairo_verifier/src/lib.cairo +++ b/stwo_cairo_verifier/src/lib.cairo @@ -2,6 +2,7 @@ mod channel; mod circle; mod fields; mod fri; +mod pcs; mod poly; mod queries; mod utils; @@ -10,3 +11,17 @@ mod vcs; pub use fields::{BaseField, SecureField}; fn main() {} + +#[derive(Clone, Drop)] +pub enum VerificationError { + /// Proof has invalid structure. + InvalidStructure: felt252, + /// Lookup values do not match. + InvalidLookup: felt252, + /// Merkle proof invalid. + Merkle: vcs::verifier::MerkleVerificationError, + /// Proof of work verification failed. + ProofOfWork, + // OodsNotMatching, +// Fri(#[from] FriVerificationError), +} diff --git a/stwo_cairo_verifier/src/pcs.cairo b/stwo_cairo_verifier/src/pcs.cairo new file mode 100644 index 000000000..1729563a2 --- /dev/null +++ b/stwo_cairo_verifier/src/pcs.cairo @@ -0,0 +1 @@ +mod quotients; diff --git a/stwo_cairo_verifier/src/pcs/quotients.cairo b/stwo_cairo_verifier/src/pcs/quotients.cairo new file mode 100644 index 000000000..c4f12f870 --- /dev/null +++ b/stwo_cairo_verifier/src/pcs/quotients.cairo @@ -0,0 +1,826 @@ +use core::array::ArrayImpl; +use core::dict::{Felt252Dict, Felt252DictEntryTrait}; +use core::iter::{IntoIterator, Iterator}; +use core::nullable::{Nullable, NullableTrait, null}; +use core::num::traits::{One, Zero}; +use stwo_cairo_verifier::VerificationError; +use stwo_cairo_verifier::circle::{ + CosetImpl, CirclePointIndexImpl, CirclePoint, M31_CIRCLE_LOG_ORDER +}; +use stwo_cairo_verifier::fields::cm31::{CM31, CM31Impl}; +use stwo_cairo_verifier::fields::m31::{M31, UnreducedM31}; +use stwo_cairo_verifier::fields::qm31::{ + QM31, QM31Impl, PackedUnreducedQM31, PackedUnreducedQM31Impl +}; +use stwo_cairo_verifier::poly::circle::{ + CircleEvaluationImpl, CircleDomain, CircleDomainImpl, SparseCircleEvaluation, + SparseCircleEvaluationImpl, CanonicCosetImpl +}; +use stwo_cairo_verifier::queries::{ + SparseSubCircleDomain, SparseSubCircleDomainImpl, SubCircleDomainImpl +}; +use stwo_cairo_verifier::utils::{bit_reverse_index, pack4, ArrayImpl as ArrayUtilImpl}; + +pub fn fri_answers( + log_size_per_column: @Array, + samples_per_column: @Array>, + random_coeff: QM31, + mut query_domain_per_log_size: Felt252Dict>, + queried_values_per_column: @Array>, +) -> Result, VerificationError> { + let n_columns = log_size_per_column.len(); + assert!(n_columns == samples_per_column.len()); + assert!(n_columns == queried_values_per_column.len()); + + // Group columns by log size. + let mut log_size_00_columns = array![]; + let mut log_size_01_columns = array![]; + let mut log_size_02_columns = array![]; + let mut log_size_03_columns = array![]; + let mut log_size_04_columns = array![]; + let mut log_size_05_columns = array![]; + let mut log_size_06_columns = array![]; + let mut log_size_07_columns = array![]; + let mut log_size_08_columns = array![]; + let mut log_size_09_columns = array![]; + let mut log_size_10_columns = array![]; + let mut log_size_11_columns = array![]; + let mut log_size_12_columns = array![]; + let mut log_size_13_columns = array![]; + let mut log_size_14_columns = array![]; + let mut log_size_15_columns = array![]; + let mut log_size_16_columns = array![]; + let mut log_size_17_columns = array![]; + let mut log_size_18_columns = array![]; + let mut log_size_19_columns = array![]; + let mut log_size_20_columns = array![]; + let mut log_size_21_columns = array![]; + let mut log_size_22_columns = array![]; + let mut log_size_23_columns = array![]; + let mut log_size_24_columns = array![]; + let mut log_size_25_columns = array![]; + let mut log_size_26_columns = array![]; + let mut log_size_27_columns = array![]; + let mut log_size_28_columns = array![]; + let mut log_size_29_columns = array![]; + let mut log_size_30_columns = array![]; + + let mut column = 0; + loop { + if column == n_columns { + break Result::Ok(()); + } + + // TODO(andrew): Order by most common for performance. i.e. check log size 16->26 first. + match *log_size_per_column[column] { + 00 => log_size_00_columns.append(column), + 01 => log_size_01_columns.append(column), + 02 => log_size_02_columns.append(column), + 03 => log_size_03_columns.append(column), + 04 => log_size_04_columns.append(column), + 05 => log_size_05_columns.append(column), + 06 => log_size_06_columns.append(column), + 07 => log_size_07_columns.append(column), + 08 => log_size_08_columns.append(column), + 09 => log_size_09_columns.append(column), + 10 => log_size_10_columns.append(column), + 11 => log_size_11_columns.append(column), + 12 => log_size_12_columns.append(column), + 13 => log_size_13_columns.append(column), + 14 => log_size_14_columns.append(column), + 15 => log_size_15_columns.append(column), + 16 => log_size_16_columns.append(column), + 17 => log_size_17_columns.append(column), + 18 => log_size_18_columns.append(column), + 19 => log_size_19_columns.append(column), + 20 => log_size_20_columns.append(column), + 21 => log_size_21_columns.append(column), + 22 => log_size_22_columns.append(column), + 23 => log_size_23_columns.append(column), + 24 => log_size_24_columns.append(column), + 25 => log_size_25_columns.append(column), + 26 => log_size_26_columns.append(column), + 27 => log_size_27_columns.append(column), + 28 => log_size_28_columns.append(column), + 29 => log_size_29_columns.append(column), + 30 => log_size_30_columns.append(column), + _ => { break Result::Err(VerificationError::InvalidStructure('invalid size')); }, + } + column += 1; + }?; + + let mut columns_per_log_size_rev = array![ + log_size_30_columns, + log_size_29_columns, + log_size_28_columns, + log_size_27_columns, + log_size_26_columns, + log_size_25_columns, + log_size_24_columns, + log_size_23_columns, + log_size_22_columns, + log_size_21_columns, + log_size_20_columns, + log_size_19_columns, + log_size_18_columns, + log_size_17_columns, + log_size_16_columns, + log_size_15_columns, + log_size_14_columns, + log_size_13_columns, + log_size_12_columns, + log_size_11_columns, + log_size_10_columns, + log_size_09_columns, + log_size_08_columns, + log_size_07_columns, + log_size_06_columns, + log_size_05_columns, + log_size_04_columns, + log_size_03_columns, + log_size_02_columns, + log_size_01_columns, + log_size_00_columns, + ] + .into_iter(); + let mut answers = array![]; + + let mut log_size = M31_CIRCLE_LOG_ORDER; + loop { + let columns = match columns_per_log_size_rev.next() { + Option::Some(columns) => columns, + Option::None => { break Result::Ok(()); }, + }; + log_size -= 1; + + if columns.is_empty() { + continue; + } + + // Collect samples and queried values for the columns. + let mut samples = array![]; + let mut queried_values = array![]; + + for column in columns { + samples.append(samples_per_column[column]); + queried_values.append(queried_values_per_column[column]); + }; + + let answer = fri_answers_for_log_size( + log_size, + samples, + random_coeff, + query_domain_per_log_size.get(log_size.into()).deref(), + queried_values + ); + + match answer { + Result::Ok(answer) => answers.append(answer), + Result::Err(err) => { break Result::Err(err); }, + }; + }?; + + Result::Ok(answers) +} + +fn fri_answers_for_log_size( + log_size: u32, + samples_per_column: Array<@Array>, + random_coeff: QM31, + query_domain: @SparseSubCircleDomain, + queried_values_per_column: Array<@Array>, +) -> Result { + let query_domain_size = query_domain.size(); + let mut queried_values_per_column_iter = queried_values_per_column.span().into_iter(); + + loop { + // Check the queries column values have the correct size. + if let Option::Some(column_queries) = queried_values_per_column_iter.next() { + if (*column_queries).len() != query_domain_size { + break Result::Err(VerificationError::InvalidStructure('queried vals')); + } + } else { + break Result::Ok(()); + } + }?; + + let commitment_domain = CanonicCosetImpl::new(log_size).circle_domain(); + let sample_batches = ColumnSampleBatchImpl::group_by_point(samples_per_column); + let quotient_consts = QuotientConstantsImpl::gen(@sample_batches, random_coeff); + let mut evals = array![]; + let mut column_eval_offset = 0; + + for subdomain in query_domain + .domains + .span() { + let domain = subdomain.to_circle_domain(commitment_domain); + let domain_size = domain.size(); + let domain_log_size = domain.log_size(); + let denominator_inverses = quotient_denominator_inverses(@sample_batches, domain); + let mut values = array![]; + + for row in 0 + ..domain_size { + let domain_point = domain.at(bit_reverse_index(row, domain_log_size)); + let value = accumulate_row_quotients( + @sample_batches, + @queried_values_per_column, + @quotient_consts, + @denominator_inverses, + row, + domain_size, + column_eval_offset, + domain_point, + ); + values.append(value); + }; + + // Progress the read offset. + column_eval_offset += domain_size; + + let eval = CircleEvaluationImpl::new(domain, values); + evals.append(eval); + }; + + assert!(column_eval_offset == query_domain_size); + + Result::Ok(SparseCircleEvaluationImpl::new(evals)) +} + +#[inline(always)] +fn accumulate_row_quotients( + sample_batches: @Array, + queried_values_per_column: @Array<@Array>, + quotient_constants: @QuotientConstants, + denominator_inverses: @Array, + row: u32, + domain_size: u32, + queries_values_offset: u32, + domain_point: CirclePoint, +) -> QM31 { + let n_batches = sample_batches.len(); + // TODO(andrew): Unnessesary asserts, remove. + assert!(n_batches == quotient_constants.line_coeffs.len()); + assert!(n_batches == quotient_constants.batch_random_coeffs.len()); + let column_row = queries_values_offset + row; + let domain_point_y: UnreducedM31 = domain_point.y.into(); + + let mut row_accumulator: QM31 = Zero::zero(); + + for batch_i in 0 + ..n_batches { + let line_coeffs = quotient_constants.line_coeffs[batch_i]; + let sample_batch_columns_and_values = sample_batches[batch_i].columns_and_values; + let batch_size = sample_batch_columns_and_values.len(); + assert!(batch_size == line_coeffs.len()); + let mut numerator: PackedUnreducedQM31 = PackedUnreducedQM31Impl::large_zero(); + + for sample_i in 0 + ..batch_size { + let (column_index, _) = sample_batch_columns_and_values[sample_i]; + let column = *queried_values_per_column.at(*column_index); + let column_value = *column.at(column_row); + let ComplexConjugateLineCoeffs { alpha_mul_a, alpha_mul_b, alpha_mul_c } = + *line_coeffs[sample_i]; + // The numerator is a line equation passing through + // (sample_point.y, sample_value), (conj(sample_point), conj(sample_value)) + // evaluated at (domain_point.y, value). + // When substituting a polynomial in this line equation, we get a polynomial + // with a root at sample_point and conj(sample_point) if the original polynomial + // had the values sample_value and conj(sample_value) at these points. + // TODO(andrew): `alpha_mul_b` can be moved out of the loop. + // TODO(andrew): The whole `linear_term` can be moved out of the loop. + let linear_term = alpha_mul_a.mul_m31(domain_point_y) + alpha_mul_b; + numerator += alpha_mul_c.mul_m31(column_value.into()) - linear_term; + }; + + let batch_coeff = *quotient_constants.batch_random_coeffs[batch_i]; + let denom_inv = *denominator_inverses[batch_i * domain_size + row]; + // Computes `row_accumulator * batch_coeff + numerator * denom_inv`. + row_accumulator = + QM31Impl::fma(row_accumulator, batch_coeff, numerator.reduce().mul_cm31(denom_inv)); + }; + + row_accumulator +} + +/// Holds the precomputed constant values used in each quotient evaluation. +#[derive(Debug, Drop)] +pub struct QuotientConstants { + /// The line coefficients for each quotient numerator term. + pub line_coeffs: Array>, + /// The random coefficients used to linearly combine the batched quotients. + /// + /// For each sample batch we compute random_coeff^(number of columns in the batch), + /// which is used to linearly combine multiple batches together. + pub batch_random_coeffs: Array, +} + +#[generate_trait] +impl QuotientConstantsImpl of QuotientConstantsTrait { + fn gen(sample_batches: @Array, random_coeff: QM31) -> QuotientConstants { + let mut line_coeffs = array![]; + let mut batch_random_coeffs = array![]; + + for sample_batch in sample_batches + .span() { + // TODO(ShaharS): Add salt. This assertion will fail at a probability of 1 to 2^62. + // Use a better solution. + assert!( + *sample_batch.point.y != (*sample_batch.point.y).complex_conjugate(), + "Cannot evaluate a line with a single point ({:?}).", + sample_batch.point + ); + + let mut alpha: QM31 = One::one(); + let mut batch_line_coeffs = array![]; + + for (_, column_value) in sample_batch + .columns_and_values + .span() { + alpha = alpha * random_coeff; + batch_line_coeffs + .append( + ComplexConjugateLineCoeffsImpl::new( + sample_batch.point, **column_value, alpha + ) + ); + }; + + batch_random_coeffs.append(alpha); + line_coeffs.append(batch_line_coeffs); + }; + + QuotientConstants { line_coeffs, batch_random_coeffs } + } +} + +/// Returns a flattened 2d array (`denom[batch_index][domain_index]`). +/// +/// Array is flattened to facilitate a batch inverse. +fn quotient_denominator_inverses( + sample_batches: @Array, domain: CircleDomain, +) -> Array { + let mut domain_points_bit_rev = array![]; + + for i in 0 + ..domain + .size() { + let i_bit_rev = bit_reverse_index(i, domain.log_size()); + domain_points_bit_rev.append(domain.at(i_bit_rev)); + }; + + let mut flat_denominators = array![]; + + // We want a `P` to be on a line that passes through a point `Pr + uPi` in `QM31^2`, + // and its conjugate `Pr - uPi`. Thus, `Pr - P` is parallel to `Pi`. Or, + // `(Pr - P).x * Pi.y - (Pr - P).y * Pi.x = 0`. + for sample_batch in sample_batches + .span() { + // Extract `Pr, Pi`. + let prx = *sample_batch.point.x.a; + let pry = *sample_batch.point.y.a; + let pix = *sample_batch.point.x.b; + let piy = *sample_batch.point.y.b; + + for domain_point in domain_points_bit_rev + .span() { + let denom = prx.sub_m31(*domain_point.x) * piy + - pry.sub_m31(*domain_point.y) * pix; + flat_denominators.append(denom); + }; + }; + + CM31Impl::batch_inverse(flat_denominators) +} + +/// A batch of column samplings at a point. +#[derive(Debug, Drop, PartialEq)] +pub struct ColumnSampleBatch { + /// The point at which the columns are sampled. + pub point: CirclePoint, + /// The sampled column indices and their values at the point. + pub columns_and_values: Array<(usize, @QM31)>, +} + +#[generate_trait] +impl ColumnSampleBatchImpl of ColumnSampleBatchTrait { + /// Groups all column samples by sampled point. + /// + /// `samples_per_column[i]` represents all point samples for column `i`. + fn group_by_point(samples_per_column: Array<@Array>) -> Array { + // Samples grouped by point. + let mut grouped_samples: Felt252Dict>> = Default::default(); + let mut point_set: Array> = array![]; + + let mut column = 0_usize; + + for samples in samples_per_column { + // TODO(andrew): Almost all columns have a single sample at the OODS point. + // Handling this case specifically is more optimal than using the dictionary. + for sample in samples + .span() { + let point_key = CirclePointQM31Key::encode(sample.point); + let (entry, point_samples) = grouped_samples.entry(point_key); + + // Check if we've seen the point before. + if point_samples.is_null() { + point_set.append(*sample.point); + }; + + let mut point_samples = point_samples.deref_or(array![]); + point_samples.append((column, sample.value)); + grouped_samples = entry.finalize(NullableTrait::new(point_samples)); + }; + + column += 1; + }; + + // TODO(andrew): Remove. Only sorting since rust verifier sorts groups by point. + let sorted_points = point_set.sort_ascending(); + let mut groups = array![]; + + for point in sorted_points { + let point_key = CirclePointQM31Key::encode(@point); + let (entry, columns_and_values) = grouped_samples.entry(point_key); + let columns_and_values = columns_and_values.deref(); + grouped_samples = entry.finalize(null()); + groups.append(ColumnSampleBatch { point, columns_and_values }); + }; + + groups + } +} + +/// The coefficients of a line between a point and its complex conjugate. Specifically, +/// `a, b, and c, s.t. a*x + b - c*y = 0` for (x,y) being (sample.y, sample.value) and +/// (conj(sample.y), conj(sample.value)). +/// Relies on the fact that every polynomial F over the base +/// field holds: F(p*) == F(p)* (* being the complex conjugate). +#[derive(Copy, Debug, Drop)] +struct ComplexConjugateLineCoeffs { + alpha_mul_a: PackedUnreducedQM31, + alpha_mul_b: PackedUnreducedQM31, + alpha_mul_c: PackedUnreducedQM31, +} + +#[generate_trait] +impl ComplexConjugateLineCoeffsImpl of ComplexConjugateLineCoeffsTrait { + fn new( + sample_point: @CirclePoint, sample_value: QM31, alpha: QM31 + ) -> ComplexConjugateLineCoeffs { + let alpha_mul_a = alpha * neg_twice_imaginary_part(@sample_value); + let alpha_mul_c = alpha * neg_twice_imaginary_part(sample_point.y); + let alpha_mul_b = QM31Impl::fms(sample_value, alpha_mul_c, alpha_mul_a * *sample_point.y); + + // TODO(andrew): These alpha multiplications are expensive. + // Think they can be saved and done all at once. + ComplexConjugateLineCoeffs { + alpha_mul_a: alpha_mul_a.into(), + alpha_mul_b: alpha_mul_b.into(), + alpha_mul_c: alpha_mul_c.into(), + } + } +} + +/// Returns `complex_conjugate(v) - v`. +#[inline] +pub fn neg_twice_imaginary_part(v: @QM31) -> QM31 { + let b = *v.b; + QM31 { a: Zero::zero(), b: -(b + b) } +} + +#[derive(Copy, Debug, Drop)] +pub struct PointSample { + pub point: CirclePoint, + pub value: QM31, +} + +/// A circle point encoding to index into [`Felt252Dict`]. +#[generate_trait] +pub impl CirclePointQM31Key of CirclePointQM31KeyTrait { + fn encode(key: @CirclePoint) -> felt252 { + let y_identifier = (*key.y.a.a.inner).into(); + pack4(y_identifier, (*key.x).to_array()) + } +} + +#[cfg(test)] +mod tests { + use core::array::ArrayImpl; + use core::dict::Felt252Dict; + use core::nullable::{NullableTrait}; + use stwo_cairo_verifier::circle::{ + QM31_CIRCLE_GEN, CosetImpl, CirclePointIndexImpl, CirclePointTrait + }; + use stwo_cairo_verifier::fields::cm31::cm31; + use stwo_cairo_verifier::fields::m31::m31; + use stwo_cairo_verifier::fields::qm31::{qm31, PackedUnreducedQM31Impl}; + use stwo_cairo_verifier::fri::CIRCLE_TO_LINE_FOLD_STEP; + use stwo_cairo_verifier::poly::circle::{ + SparseCircleEvaluationImpl, CanonicCosetImpl, CircleDomainImpl, CircleEvaluationImpl + }; + use stwo_cairo_verifier::queries::SubCircleDomainImpl; + use stwo_cairo_verifier::queries::{SparseSubCircleDomain, SubCircleDomain}; + use stwo_cairo_verifier::utils::{pow, DictImpl}; + use super::{ + PointSample, fri_answers_for_log_size, ColumnSampleBatch, ColumnSampleBatchImpl, + ComplexConjugateLineCoeffsImpl, quotient_denominator_inverses, accumulate_row_quotients, + QuotientConstantsImpl, fri_answers + }; + + + #[test] + fn test_fri_answers_for_log_size() { + let log_size = 5; + let commitment_domain = CanonicCosetImpl::new(log_size).circle_domain(); + let sample0 = PointSample { point: QM31_CIRCLE_GEN, value: qm31(0, 1, 2, 3) }; + let sample1 = PointSample { point: QM31_CIRCLE_GEN.mul(2), value: qm31(1, 2, 3, 4) }; + let sample2 = PointSample { point: QM31_CIRCLE_GEN.mul(3), value: qm31(2, 3, 4, 5) }; + let col0_samples = array![sample0, sample1, sample2]; + let col1_samples = array![sample0]; + let col2_samples = array![sample0, sample2]; + let samples_per_column = array![@col0_samples, @col1_samples, @col2_samples]; + let random_coeff = qm31(9, 8, 7, 6); + let sub_domain0 = SubCircleDomain { coset_index: 2, log_size: 1 }; + let sub_domain1 = SubCircleDomain { coset_index: 3, log_size: 1 }; + let query_domain = SparseSubCircleDomain { + domains: array![sub_domain0, sub_domain1], large_domain_log_size: log_size + }; + let col0_query_values = array![m31(1), m31(2), m31(3), m31(4)]; + let col1_query_values = array![m31(1), m31(1), m31(2), m31(3)]; + let col2_query_values = array![m31(1), m31(1), m31(1), m31(2)]; + let queried_values_per_column = array![ + @col0_query_values, @col1_query_values, @col2_query_values + ]; + + let res = fri_answers_for_log_size( + log_size, samples_per_column, random_coeff, @query_domain, queried_values_per_column + ) + .unwrap(); + + assert!( + res == SparseCircleEvaluationImpl::new( + array![ + CircleEvaluationImpl::new( + (@sub_domain0).to_circle_domain(commitment_domain), + array![ + qm31(1655798290, 1221610097, 1389601557, 962654234), + qm31(638770057, 234503953, 730529691, 1759474677) + ] + ), + CircleEvaluationImpl::new( + (@sub_domain1).to_circle_domain(commitment_domain), + array![ + qm31(812355951, 1467349841, 519312011, 1870584702), + qm31(1802072315, 1125204194, 422281582, 1308225981) + ] + ), + ] + ) + ); + } + + #[test] + fn test_fri_answers() { + let col0_log_size = 5; + let col1_log_size = 7; + let log_size_per_column = array![col0_log_size, col1_log_size]; + let col0_commitment_domain = CanonicCosetImpl::new(col0_log_size).circle_domain(); + let col1_commitment_domain = CanonicCosetImpl::new(col1_log_size).circle_domain(); + let sample0 = PointSample { point: QM31_CIRCLE_GEN, value: qm31(0, 1, 2, 3) }; + let sample1 = PointSample { point: QM31_CIRCLE_GEN.mul(2), value: qm31(1, 2, 3, 4) }; + let col0_samples = array![sample0, sample1]; + let col1_samples = array![sample0]; + let samples_per_column = array![col0_samples, col1_samples]; + let random_coeff = qm31(9, 8, 7, 6); + let col0_sub_domain = SubCircleDomain { coset_index: 2, log_size: 1 }; + let col0_query_domain = SparseSubCircleDomain { + domains: array![col0_sub_domain], large_domain_log_size: col0_log_size + }; + let col1_sub_domain = SubCircleDomain { coset_index: 3, log_size: 1 }; + let col1_query_domain = SparseSubCircleDomain { + domains: array![col1_sub_domain], large_domain_log_size: col1_log_size + }; + let mut query_domain_per_log_size: Felt252Dict = Default::default(); + query_domain_per_log_size.insert(5, NullableTrait::new(@col0_query_domain)); + query_domain_per_log_size.replace(7, NullableTrait::new(@col1_query_domain)); + let col0_query_values = array![m31(9), m31(2)]; + let col1_query_values = array![m31(3), m31(7)]; + let queried_values_per_column = array![col0_query_values, col1_query_values]; + + let res = fri_answers( + @log_size_per_column, + @samples_per_column, + random_coeff, + query_domain_per_log_size, + @queried_values_per_column + ) + .unwrap(); + + assert!( + res == array![ + SparseCircleEvaluationImpl::new( + array![ + CircleEvaluationImpl::new( + (@col1_sub_domain).to_circle_domain(col1_commitment_domain), + array![ + qm31(1791306293, 1053124067, 158259497, 452720916), + qm31(212478330, 1383090185, 1622369493, 599681801), + ] + ), + ] + ), + SparseCircleEvaluationImpl::new( + array![ + CircleEvaluationImpl::new( + (@col0_sub_domain).to_circle_domain(col0_commitment_domain), + array![ + qm31(834593128, 54438530, 120431711, 2027138945), + qm31(1820575540, 1615656673, 695030281, 674192396) + ] + ), + ] + ) + ] + ); + } + + #[test] + fn test_complex_conjugate_line_coeffs_impl() { + let point = QM31_CIRCLE_GEN; + let value = qm31(9, 8, 7, 6); + let alpha = qm31(2, 3, 4, 5); + + let res = ComplexConjugateLineCoeffsImpl::new(@point, value, alpha); + + assert!(res.alpha_mul_a.reduce() == qm31(126, 2147483415, 8, 2147483581)); + assert!(res.alpha_mul_b.reduce() == qm31(20238140, 1378415613, 17263450, 142791233)); + assert!(res.alpha_mul_c.reduce() == qm31(865924731, 72415967, 2011255989, 1549931113)); + } + + #[test] + fn test_quotient_denominator_inverses() { + let domain = CircleDomainImpl::new(CosetImpl::new(CirclePointIndexImpl::new(1), 0)); + let samples = array![ + ColumnSampleBatch { point: QM31_CIRCLE_GEN, columns_and_values: array![] }, + ColumnSampleBatch { point: QM31_CIRCLE_GEN.mul(2), columns_and_values: array![] }, + ]; + + let denominator_inverses = quotient_denominator_inverses(@samples, domain); + + assert!( + denominator_inverses == array![ + cm31(432303227, 706927115), + cm31(1241984415, 2002674046), + cm31(710698435, 1874662077), + cm31(805681622, 1895046717) + ] + ); + } + + #[test] + fn test_column_sample_batch_group_by_point() { + let sample0 = PointSample { point: QM31_CIRCLE_GEN, value: qm31(0, 1, 2, 3) }; + let sample1 = PointSample { point: QM31_CIRCLE_GEN.mul(2), value: qm31(1, 2, 3, 4) }; + let sample2 = PointSample { point: QM31_CIRCLE_GEN.mul(3), value: qm31(2, 3, 4, 5) }; + let col0_samples = array![sample0, sample1, sample2]; + let col1_samples = array![sample0]; + let col2_samples = array![sample0, sample2]; + let samples_per_column = array![@col0_samples, @col1_samples, @col2_samples]; + + let grouped_samples = ColumnSampleBatchImpl::group_by_point(samples_per_column); + + assert!( + grouped_samples == array![ + ColumnSampleBatch { + point: sample0.point, + columns_and_values: array![ + (0, @sample0.value), (1, @sample0.value), (2, @sample0.value) + ], + }, + ColumnSampleBatch { + point: sample2.point, + columns_and_values: array![(0, @sample2.value), (2, @sample2.value)], + }, + ColumnSampleBatch { + point: sample1.point, columns_and_values: array![(0, @sample1.value)], + }, + ] + ) + } + + #[test] + fn test_accumulate_row_quotients() { + let alpha = qm31(4, 3, 2, 1); + let domain = CircleDomainImpl::new(CosetImpl::new(CirclePointIndexImpl::new(1), 0)); + let column1_query_values = array![m31(1), m31(9)]; + let column0_query_values = array![m31(5), m31(3)]; + let queried_values_per_column = array![@column0_query_values, @column1_query_values]; + let sample_batches = array![ + ColumnSampleBatch { + point: QM31_CIRCLE_GEN, columns_and_values: array![(0, @qm31(0, 1, 2, 3))] + }, + ColumnSampleBatch { + point: QM31_CIRCLE_GEN.mul(2), columns_and_values: array![(1, @qm31(1, 2, 3, 4))] + } + ]; + let quotient_constants = QuotientConstantsImpl::gen(@sample_batches, alpha); + let denominator_inverses = quotient_denominator_inverses(@sample_batches, domain); + let row = 0; + + let res = accumulate_row_quotients( + @sample_batches, + @queried_values_per_column, + @quotient_constants, + @denominator_inverses, + row, + domain.size(), + 0, + domain.at(0) + ); + + assert!(res == qm31(545815778, 838613809, 1761463254, 2019099482)); + } + + #[test] + fn test_accumulate_row_quotients_with_offset() { + let alpha = qm31(4, 3, 2, 1); + let domain = CircleDomainImpl::new(CosetImpl::new(CirclePointIndexImpl::new(1), 0)); + let column1_query_values = array![m31(1), m31(9), m31(2), m31(5)]; + let column0_query_values = array![m31(5), m31(3), m31(3), m31(1)]; + let queried_values_per_column = array![@column0_query_values, @column1_query_values]; + let sample_batches = array![ + ColumnSampleBatch { + point: QM31_CIRCLE_GEN, columns_and_values: array![(0, @qm31(0, 1, 2, 3))] + }, + ColumnSampleBatch { + point: QM31_CIRCLE_GEN.mul(2), columns_and_values: array![(1, @qm31(1, 2, 3, 4))] + } + ]; + let quotient_constants = QuotientConstantsImpl::gen(@sample_batches, alpha); + let denominator_inverses = quotient_denominator_inverses(@sample_batches, domain); + let column_eval_offset = 2; + let row = 1; + + let res = accumulate_row_quotients( + @sample_batches, + @queried_values_per_column, + @quotient_constants, + @denominator_inverses, + row, + domain.size(), + column_eval_offset, + domain.at(1) + ); + + assert!(res == qm31(1352199520, 303329565, 518279043, 1496238271)); + } + + // Test used to benchmark step counts. + #[test] + fn test_fri_answers_with_1000_columns() { + // TODO(andrew): Note Forge fails if these are declated `const ...`. + let log_size = 16; + let n_queries = 20; + let n_columns = 1000; + let log_fold_step = CIRCLE_TO_LINE_FOLD_STEP; + let random_coeff = qm31(9, 8, 7, 6); + assert!(n_queries < pow(2, log_size), "Query indices need to be unique"); + assert!(n_columns >= 3, "First three columns are manually created"); + let mut query_subdomains = array![]; + for coset_index in 0 + ..n_queries { + query_subdomains.append(SubCircleDomain { coset_index, log_size: log_fold_step }) + }; + let query_domain = SparseSubCircleDomain { + domains: query_subdomains, large_domain_log_size: log_size + }; + let sample0 = PointSample { point: QM31_CIRCLE_GEN, value: qm31(0, 1, 2, 3) }; + let sample1 = PointSample { point: QM31_CIRCLE_GEN.mul(2), value: qm31(1, 2, 3, 4) }; + let sample2 = PointSample { point: QM31_CIRCLE_GEN.mul(3), value: qm31(2, 3, 4, 5) }; + let col0_samples = array![sample0, sample1, sample2]; + let col1_samples = array![sample0]; + let col2_samples = array![sample0, sample2]; + let mut samples_per_column = array![@col0_samples, @col1_samples, @col2_samples]; + let mut col_query_values = array![]; + for i in 0..n_queries * pow(2, log_fold_step) { + col_query_values.append(m31(i)); + }; + let col0_query_values = col_query_values.clone(); + let col1_query_values = col_query_values.clone(); + let col2_query_values = col_query_values.clone(); + let mut queried_values_per_column = array![ + @col0_query_values, @col1_query_values, @col2_query_values + ]; + for _ in samples_per_column.len() + ..n_columns { + samples_per_column.append(@col1_samples); + queried_values_per_column.append(@col_query_values); + }; + + let _res = fri_answers_for_log_size( + log_size, samples_per_column, random_coeff, @query_domain, queried_values_per_column + ); + } +} diff --git a/stwo_cairo_verifier/src/queries.cairo b/stwo_cairo_verifier/src/queries.cairo index 56560cd21..e7c5feb7b 100644 --- a/stwo_cairo_verifier/src/queries.cairo +++ b/stwo_cairo_verifier/src/queries.cairo @@ -122,6 +122,10 @@ pub impl SubCircleDomainImpl of SubCircleDomainTrait { let half_coset = CosetImpl::new(initial_index, *self.log_size - 1); CircleDomainImpl::new(half_coset) } + + fn size(self: @SubCircleDomain) -> usize { + pow(2, *self.log_size).into() + } } #[derive(Drop, Debug)] @@ -143,6 +147,15 @@ pub impl SparseSubCircleDomainImpl of SparseSubCircleDomainTrait { }; res } + + /// Returns the number of points in the domain. + fn size(self: @SparseSubCircleDomain) -> usize { + let mut size = 0; + for domain in self.domains.span() { + size += domain.size(); + }; + size + } } #[cfg(test)] From 15763bd8cc68541e5fef8f7c419d381ea57b8761 Mon Sep 17 00:00:00 2001 From: yuvalsw <105596564+yuvalsw@users.noreply.github.com> Date: Mon, 21 Oct 2024 19:49:27 +0300 Subject: [PATCH 09/27] Added logs for checkpoints in VM-runner (#108) --- stwo_cairo_prover/Cargo.lock | 169 ++++++++++++++++++ stwo_cairo_prover/Cargo.toml | 6 +- stwo_cairo_prover/crates/utils/Cargo.toml | 10 ++ stwo_cairo_prover/crates/utils/src/lib.rs | 1 + .../crates/utils/src/logging_utils.rs | 13 ++ stwo_cairo_prover/crates/vm_runner/Cargo.toml | 7 +- .../crates/vm_runner/src/main.rs | 11 +- 7 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 stwo_cairo_prover/crates/utils/Cargo.toml create mode 100644 stwo_cairo_prover/crates/utils/src/lib.rs create mode 100644 stwo_cairo_prover/crates/utils/src/logging_utils.rs diff --git a/stwo_cairo_prover/Cargo.lock b/stwo_cairo_prover/Cargo.lock index c7784996a..789738d53 100644 --- a/stwo_cairo_prover/Cargo.lock +++ b/stwo_cairo_prover/Cargo.lock @@ -32,6 +32,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -526,6 +535,29 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -620,6 +652,12 @@ dependencies = [ "digest", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -788,6 +826,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -851,6 +899,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -993,6 +1047,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1093,6 +1176,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1249,6 +1341,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "stwo_cairo_utils" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", + "tracing", + "tracing-subscriber", +] + [[package]] name = "stwo_vm_runner" version = "0.1.0" @@ -1256,10 +1358,13 @@ dependencies = [ "cairo-vm", "clap", "itertools 0.12.1", + "log", "num-traits", "serde", "stwo_cairo_prover", + "stwo_cairo_utils", "thiserror", + "tracing", "wasm-bindgen", ] @@ -1337,6 +1442,16 @@ dependencies = [ "thiserror-impl-no-std", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -1383,6 +1498,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -1403,6 +1544,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.5" @@ -1469,6 +1616,28 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/stwo_cairo_prover/Cargo.toml b/stwo_cairo_prover/Cargo.toml index 8cf01e758..adcc92683 100644 --- a/stwo_cairo_prover/Cargo.toml +++ b/stwo_cairo_prover/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/adapted_prover", "crates/prover", "crates/vm_runner"] +members = ["crates/adapted_prover", "crates/prover", "crates/utils", "crates/vm_runner"] resolver = "2" [workspace.package] @@ -12,8 +12,10 @@ cairo-lang-casm = "2.7.1" # TODO(yuval): Use an official version, not a specific commit. cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "3fb0344c", features = ["mod_builtin"]} clap = { version = "4.3.10", features = ["derive"] } +env_logger = "0.11.3" hex = "0.4.3" itertools = "0.12.0" +log = "0.4.21" num-traits = "0.2.17" rand = "0.8.5" serde = "1.0.207" @@ -22,6 +24,8 @@ sonic-rs = "0.3.10" # TODO(ShaharS): take stwo version from the source repository. stwo-prover = { git = "https://github.com/starkware-libs/stwo", rev = "e04fd5b" } thiserror = "1.0.63" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" [profile.bench] diff --git a/stwo_cairo_prover/crates/utils/Cargo.toml b/stwo_cairo_prover/crates/utils/Cargo.toml new file mode 100644 index 000000000..678150182 --- /dev/null +++ b/stwo_cairo_prover/crates/utils/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "stwo_cairo_utils" +version = "0.1.0" +edition = "2021" + +[dependencies] +env_logger.workspace = true +log.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true diff --git a/stwo_cairo_prover/crates/utils/src/lib.rs b/stwo_cairo_prover/crates/utils/src/lib.rs new file mode 100644 index 000000000..5d46e2e77 --- /dev/null +++ b/stwo_cairo_prover/crates/utils/src/lib.rs @@ -0,0 +1 @@ +pub mod logging_utils; diff --git a/stwo_cairo_prover/crates/utils/src/logging_utils.rs b/stwo_cairo_prover/crates/utils/src/logging_utils.rs new file mode 100644 index 000000000..33fd10d7b --- /dev/null +++ b/stwo_cairo_prover/crates/utils/src/logging_utils.rs @@ -0,0 +1,13 @@ +use tracing_subscriber::fmt::format::FmtSpan; + +/// Initializes env_logger. +/// The format is: +/// ` /path/to/file: