From 87a5c996dbe578813524e9d5c12bf36c739829ae Mon Sep 17 00:00:00 2001 From: Shahar Papini Date: Wed, 24 Apr 2024 16:49:37 +0300 Subject: [PATCH] M31 and CM31 Fields --- .github/workflows/cairo-ci.yaml | 30 +++++++ stwo_cairo_verifier/.gitignore | 1 + stwo_cairo_verifier/Scarb.lock | 6 ++ stwo_cairo_verifier/Scarb.toml | 8 ++ stwo_cairo_verifier/src/fields.cairo | 2 + stwo_cairo_verifier/src/fields/cm31.cairo | 83 ++++++++++++++++++++ stwo_cairo_verifier/src/fields/m31.cairo | 96 +++++++++++++++++++++++ stwo_cairo_verifier/src/lib.cairo | 3 + 8 files changed, 229 insertions(+) create mode 100644 .github/workflows/cairo-ci.yaml create mode 100644 stwo_cairo_verifier/.gitignore create mode 100644 stwo_cairo_verifier/Scarb.lock create mode 100644 stwo_cairo_verifier/Scarb.toml create mode 100644 stwo_cairo_verifier/src/fields.cairo create mode 100644 stwo_cairo_verifier/src/fields/cm31.cairo create mode 100644 stwo_cairo_verifier/src/fields/m31.cairo create mode 100644 stwo_cairo_verifier/src/lib.cairo diff --git a/.github/workflows/cairo-ci.yaml b/.github/workflows/cairo-ci.yaml new file mode 100644 index 00000000..d939916d --- /dev/null +++ b/.github/workflows/cairo-ci.yaml @@ -0,0 +1,30 @@ +name: Cairo workflow +on: + push: + branches: + - main + + pull_request: + types: + - opened + - reopened + - synchronize + - auto_merge_enabled + + merge_group: + types: + - checks_requested + +jobs: + scarb-test: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./stwo_cairo_verifier + steps: + - uses: actions/checkout@v3 + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.6.4" + - run: scarb fmt --check + - run: scarb test diff --git a/stwo_cairo_verifier/.gitignore b/stwo_cairo_verifier/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/stwo_cairo_verifier/.gitignore @@ -0,0 +1 @@ +target diff --git a/stwo_cairo_verifier/Scarb.lock b/stwo_cairo_verifier/Scarb.lock new file mode 100644 index 00000000..0c9db427 --- /dev/null +++ b/stwo_cairo_verifier/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "stwo_cairo_verifier" +version = "0.1.0" diff --git a/stwo_cairo_verifier/Scarb.toml b/stwo_cairo_verifier/Scarb.toml new file mode 100644 index 00000000..53c84a92 --- /dev/null +++ b/stwo_cairo_verifier/Scarb.toml @@ -0,0 +1,8 @@ +[package] +name = "stwo_cairo_verifier" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] diff --git a/stwo_cairo_verifier/src/fields.cairo b/stwo_cairo_verifier/src/fields.cairo new file mode 100644 index 00000000..57547036 --- /dev/null +++ b/stwo_cairo_verifier/src/fields.cairo @@ -0,0 +1,2 @@ +pub mod m31; +pub mod cm31; diff --git a/stwo_cairo_verifier/src/fields/cm31.cairo b/stwo_cairo_verifier/src/fields/cm31.cairo new file mode 100644 index 00000000..75373eba --- /dev/null +++ b/stwo_cairo_verifier/src/fields/cm31.cairo @@ -0,0 +1,83 @@ +use super::m31::{M31, m31}; + +#[derive(Copy, Drop, Debug, PartialEq, Eq)] +pub struct CM31 { + a: M31, + b: M31, +} + +pub impl CM31Add of core::traits::Add { + 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 { + 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 { + 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 CM31Zero of core::num::traits::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 core::num::traits::One { + fn one() -> CM31 { + cm31(1, 0) + } + fn is_one(self: @CM31) -> bool { + (*self).a.is_one() && (*self).b.is_zero() + } + fn is_non_one(self: @CM31) -> bool { + (*self).a.is_non_one() || (*self).b.is_non_zero() + } +} +pub impl M31IntoCM31 of core::traits::Into { + fn into(self: M31) -> CM31 { + CM31 { a: self, b: m31(0) } + } +} +pub impl CM31Neg of Neg { + fn neg(a: CM31) -> CM31 { + CM31 { a: -a.a, b: -a.b } + } +} + +pub fn cm31(a: u32, b: u32) -> CM31 { + CM31 { a: m31(a), b: m31(b) } +} + + +#[cfg(test)] +mod tests { + use super::{cm31}; + use super::super::m31::{m31, P}; + + #[test] + fn test_m31() { + let cm0 = cm31(1, 2); + let cm1 = cm31(4, 5); + let m = m31(8); + let cm = cm31(8, 0); + let cm0_x_cm1 = cm31(P - 6, 13); + + assert_eq!(cm0 + cm1, cm31(5, 7)); + assert_eq!(cm1 + m.into(), cm1 + cm); + assert_eq!(cm0 * cm1, cm0_x_cm1); + assert_eq!(cm1 * m.into(), cm1 * cm); + assert_eq!(-cm0, cm31(P - 1, P - 2)); + assert_eq!(cm0 - cm1, cm31(P - 3, P - 3)); + assert_eq!(cm1 - m.into(), cm1 - cm); + } +} diff --git a/stwo_cairo_verifier/src/fields/m31.cairo b/stwo_cairo_verifier/src/fields/m31.cairo new file mode 100644 index 00000000..58924430 --- /dev/null +++ b/stwo_cairo_verifier/src/fields/m31.cairo @@ -0,0 +1,96 @@ +use core::option::OptionTrait; +use core::traits::TryInto; +use core::result::ResultTrait; + +pub const P: u32 = 0x7fffffff; +const P32NZ: NonZero = 0x7fffffff; +const P64NZ: NonZero = 0x7fffffff; + +#[derive(Copy, Drop, Debug, PartialEq, Eq)] +pub struct M31 { + inner: u32 +} + +#[generate_trait] +pub impl M31Impl of M31Trait { + fn reduce_u32(val: u32) -> M31 { + let (_, res) = core::integer::u32_safe_divmod(val, P32NZ); + M31 { inner: res.try_into().unwrap() } + } + + fn reduce_u64(val: u64) -> M31 { + let (_, res) = core::integer::u64_safe_divmod(val, P64NZ); + M31 { inner: res.try_into().unwrap() } + } +} +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); + M31 { inner: res } + } +} +pub impl M31Sub of core::traits::Sub { + fn sub(lhs: M31, rhs: M31) -> M31 { + match core::integer::u32_overflowing_sub(lhs.inner, rhs.inner) { + Result::Ok(res) => M31 { inner: res }, + Result::Err(res) => M31 { inner: core::integer::u32_wrapping_add(res, P) } + } + } +} +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)) + } +} +pub impl M31Zero of core::num::traits::Zero { + 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 { + 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 { + fn neg(a: M31) -> M31 { + if a.inner == 0 { + M31 { inner: 0 } + } else { + M31 { inner: P - a.inner } + } + } +} + +pub fn m31(val: u32) -> M31 { + M31Impl::reduce_u32(val) +} + +#[cfg(test)] +mod tests { + use super::{m31, P}; + + #[test] + fn test_m31() { + assert_eq!(m31(P), m31(0)); + assert_eq!(m31(P + 1), m31(1)); + assert_eq!(m31(1) + m31(2), m31(3)); + assert_eq!(m31(3) - m31(2), m31(1)); + assert_eq!(m31(P - 1) + m31(1), m31(0)); + assert_eq!(m31(0) - m31(1), m31(P - 1)); + assert_eq!(m31(0) - m31(P - 1), m31(1)); + } +} diff --git a/stwo_cairo_verifier/src/lib.cairo b/stwo_cairo_verifier/src/lib.cairo new file mode 100644 index 00000000..e95030d6 --- /dev/null +++ b/stwo_cairo_verifier/src/lib.cairo @@ -0,0 +1,3 @@ +mod fields; + +fn main() {}