diff --git a/src/lib.rs b/src/lib.rs index 0c73fdc..45c0236 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate alloc; // ─── Generic field trait ───────────────────────────────────────────────────── pub mod field; +#[macro_use] +mod macros; pub mod proof; // ─── New canonical API (Thaler §4.1) ──────────────────────────────────────── diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..d7da7dc --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,132 @@ +//! Exported convenience macros. + +/// Define a thin newtype wrapper around a field type and implement +/// [`SumcheckField`](crate::field::SumcheckField) for it. +/// +/// This is useful when integrating a field type from another ecosystem whose +/// trait impls cannot be implemented directly because of Rust's orphan rules. +/// +/// ```ignore +/// effsc::sumcheck_field_newtype! { +/// pub struct MyField(OtherCrateField); +/// const ZERO = OtherCrateField::ZERO; +/// const ONE = OtherCrateField::ONE; +/// fn from_u64(val) { OtherCrateField::from_canonical_u64(val) } +/// fn inverse(self) { self.0.try_inverse() } +/// } +/// ``` +#[macro_export] +macro_rules! sumcheck_field_newtype { + ( + $(#[$meta:meta])* + $vis:vis struct $name:ident($inner:ty); + const ZERO = $zero:expr; + const ONE = $one:expr; + fn from_u64($from_arg:ident) $from_u64:block + fn inverse($self_arg:ident) $inverse:block + ) => { + $(#[$meta])* + #[repr(transparent)] + #[derive(Copy, Clone, Debug, PartialEq)] + $vis struct $name($inner); + + impl $name { + #[inline] + pub fn new(val: u64) -> Self { + ::from_u64(val) + } + + #[inline] + pub const fn from_inner(inner: $inner) -> Self { + Self(inner) + } + + #[inline] + pub fn into_inner(self) -> $inner { + self.0 + } + } + + impl core::fmt::Display for $name { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.0) + } + } + + impl core::ops::Add for $name { + type Output = Self; + #[inline] + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } + } + + impl core::ops::Sub for $name { + type Output = Self; + #[inline] + fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0) + } + } + + impl core::ops::Mul for $name { + type Output = Self; + #[inline] + fn mul(self, rhs: Self) -> Self { + Self(self.0 * rhs.0) + } + } + + impl core::ops::Neg for $name { + type Output = Self; + #[inline] + fn neg(self) -> Self { + Self(-self.0) + } + } + + impl core::ops::AddAssign for $name { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } + } + + impl core::ops::SubAssign for $name { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } + } + + impl core::ops::MulAssign for $name { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + self.0 *= rhs.0; + } + } + + impl core::iter::Sum for $name { + #[inline] + fn sum>(iter: I) -> Self { + iter.fold(::ZERO, |acc, x| acc + x) + } + } + + impl $crate::field::SumcheckField for $name { + const ZERO: Self = Self($zero); + const ONE: Self = Self($one); + + #[inline] + fn from_u64($from_arg: u64) -> Self { + Self($from_u64) + } + + #[inline] + fn inverse(&$self_arg) -> Option { + $inverse.map(Self) + } + } + }; +} diff --git a/tests/plonky3_roundtrip.rs b/tests/plonky3_roundtrip.rs index 7dbfb87..ddfa561 100644 --- a/tests/plonky3_roundtrip.rs +++ b/tests/plonky3_roundtrip.rs @@ -3,10 +3,10 @@ //! Shows that any ecosystem's field type works with the sumcheck library //! via a thin `SumcheckField` impl — no arkworks dependency required. -use effsc::field::SumcheckField; use effsc::noop_hook; use effsc::provers::multilinear_lsb::MultilinearProverLSB; use effsc::runner::sumcheck; +use effsc::sumcheck_field_newtype; use effsc::transcript::{ProverTranscript, VerifierTranscript}; use effsc::verifier::sumcheck_verify; @@ -14,91 +14,15 @@ use p3_field::integers::QuotientMap; use p3_field::Field; use p3_goldilocks::Goldilocks; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - // ─── Newtype wrapper ─────────────────────────────────────────────────────── -/// Thin wrapper around Plonky3's Goldilocks to implement `SumcheckField`. -#[derive(Copy, Clone, Debug, PartialEq)] -struct P3Goldilocks(Goldilocks); - -impl P3Goldilocks { - fn new(val: u64) -> Self { - Self(Goldilocks::from_int(val)) - } -} - -impl fmt::Display for P3Goldilocks { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Add for P3Goldilocks { - type Output = Self; - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) - } -} - -impl Sub for P3Goldilocks { - type Output = Self; - fn sub(self, rhs: Self) -> Self { - Self(self.0 - rhs.0) - } -} - -impl Mul for P3Goldilocks { - type Output = Self; - fn mul(self, rhs: Self) -> Self { - Self(self.0 * rhs.0) - } -} - -impl Neg for P3Goldilocks { - type Output = Self; - fn neg(self) -> Self { - Self(-self.0) - } -} - -impl AddAssign for P3Goldilocks { - fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0; - } -} - -impl SubAssign for P3Goldilocks { - fn sub_assign(&mut self, rhs: Self) { - self.0 -= rhs.0; - } -} - -impl MulAssign for P3Goldilocks { - fn mul_assign(&mut self, rhs: Self) { - self.0 *= rhs.0; - } -} - -impl Sum for P3Goldilocks { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |acc, x| acc + x) - } -} - -impl SumcheckField for P3Goldilocks { - const ZERO: Self = Self(Goldilocks::new(0)); - const ONE: Self = Self(Goldilocks::new(1)); - - fn from_u64(val: u64) -> Self { - Self(Goldilocks::from_int(val)) - } - - fn inverse(&self) -> Option { - self.0.try_inverse().map(Self) - } +sumcheck_field_newtype! { + /// Thin wrapper around Plonky3's Goldilocks to implement `SumcheckField`. + struct P3Goldilocks(Goldilocks); + const ZERO = Goldilocks::new(0); + const ONE = Goldilocks::new(1); + fn from_u64(val) { Goldilocks::from_int(val) } + fn inverse(self) { self.0.try_inverse() } } // ─── Minimal transcript ────────────────────────────────────────────────────