Skip to content

Commit

Permalink
Goldilocks implementation in Rust (risc0#250)
Browse files Browse the repository at this point in the history
* including Goldilocks

* Adding Goldilocks field

* simplifying test for native operations

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Frank Laub <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Tim Zerrell <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Tim Zerrell <[email protected]>

* updated Goldilocks with suggested changes

* updating comments in  and

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Frank Laub <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Frank Laub <[email protected]>

* adding comment to explain Goldilocks field calculation

* Update risc0/zkp/rust/src/field/baby_bear.rs

Co-authored-by: Tim Zerrell <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Tim Zerrell <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Tim Zerrell <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Frank Laub <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Frank Laub <[email protected]>

* Update risc0/zkp/rust/src/field/goldilocks.rs

Co-authored-by: Tim Zerrell <[email protected]>

* documentation for fields

* adding descriptions for Elem, ExtElem, and RootsOfUnity

* giving tests more descriptive names

Co-authored-by: 3lkn <[email protected]>
Co-authored-by: Frank Laub <[email protected]>
Co-authored-by: Tim Zerrell <[email protected]>
  • Loading branch information
4 people authored Aug 25, 2022
1 parent 62db8c8 commit 3f2c728
Show file tree
Hide file tree
Showing 3 changed files with 826 additions and 44 deletions.
83 changes: 51 additions & 32 deletions risc0/zkp/rust/src/field/baby_bear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

/// ! Baby bear field.
/// ! Support for the base finite field modulo 15*2^27 + 1
use crate::field::{self, Elem as FieldElem};

use alloc::fmt;
use core::ops;

use bytemuck::{Pod, Zeroable};

/// ! Baby bear field.
/// ! Support for the base finite field modulo 15*2^27 + 1
use crate::field::{self, Elem as FieldElem};

// montgomery form constants
const M: u32 = 0x88000001;
const R2: u32 = 1172168163;
Expand Down Expand Up @@ -67,20 +67,20 @@ const P_U64: u64 = P as u64;

impl field::Elem for Elem {
const ZERO: Self = Elem::new(0);

const ONE: Self = Elem::new(1);

/// Compute the multiplicative inverse of `x`, or `1 / x` in finite field
/// terms. Since `x ^ (P - 1) == 1 % P` for any `x != 0` (as a
/// consequence of Fermat's little theorem), it follows that `x *
/// x ^ (P - 2) == 1 % P` for `x != 0`. That is, `x ^ (P - 2)` is the
/// multiplicative inverse of `x`. Computed this way, the *inverse* of
/// zero comes out as zero, which is convenient in many cases, so we
/// leave it.
/// terms. Since we know by Fermat's Little Theorem that
/// `x ^ (P - 1) == 1 % P` for any `x != 0`,
/// it follows that `x * x ^ (P - 2) == 1 % P` for `x != 0`.
/// That is, `x ^ (P - 2)` is the multiplicative inverse of `x`.
/// Note that if computed this way, the *inverse* of zero comes out as zero,
/// which we allow because it is convenient in many cases.
fn inv(self) -> Self {
self.pow((P - 2) as usize)
}

/// Generate a random value within the Baby Bear field
fn random(rng: &mut impl rand::Rng) -> Self {
// Reject the last modulo-P region of possible uint32_t values, since it's
// uneven and will only return random values less than (2^32 % P).
Expand All @@ -101,15 +101,19 @@ macro_rules! rou_array {
}

impl field::RootsOfUnity for Elem {
/// Maximum power of two for which we have a root of unity using Baby Bear
/// field
const MAX_ROU_PO2: usize = 27;

/// 'Forward' root of unity for each power of two.
const ROU_FWD: &'static [Elem] = &rou_array![
1, 2013265920, 284861408, 1801542727, 567209306, 740045640, 918899846, 1881002012,
1453957774, 65325759, 1538055801, 515192888, 483885487, 157393079, 1695124103, 2005211659,
1540072241, 88064245, 1542985445, 1269900459, 1461624142, 825701067, 682402162, 1311873874,
1164520853, 352275361, 18769, 137
];

/// 'Reverse' root of unity for each power of two.
const ROU_REV: &'static [Elem] = &rou_array![
1, 2013265920, 1728404513, 1592366214, 196396260, 1253260071, 72041623, 1091445674,
145223211, 1446820157, 1030796471, 2010749425, 1827366325, 1239938613, 246299276,
Expand All @@ -124,7 +128,7 @@ impl Elem {
Self(encode(x % P))
}

/// Return the montgomery form representation used for byte-based
/// Return the Montgomery form representation used for byte-based
/// hashes of slices of [BabyBear]s.
pub const fn as_u32_montgomery(&self) -> u32 {
self.0
Expand All @@ -133,38 +137,44 @@ impl Elem {

impl ops::Add for Elem {
type Output = Self;
/// Addition for Baby Bear [Elem]
fn add(self, rhs: Self) -> Self {
Elem(add(self.0, rhs.0))
}
}

impl ops::AddAssign for Elem {
/// Simple addition case for Baby Bear [Elem]
fn add_assign(&mut self, rhs: Self) {
self.0 = add(self.0, rhs.0)
}
}

impl ops::Sub for Elem {
type Output = Self;
/// Subtraction for Baby Bear [Elem]
fn sub(self, rhs: Self) -> Self {
Elem(sub(self.0, rhs.0))
}
}

impl ops::SubAssign for Elem {
/// Simple subtraction case for Baby Bear [Elem]
fn sub_assign(&mut self, rhs: Self) {
self.0 = sub(self.0, rhs.0)
}
}

impl ops::Mul for Elem {
type Output = Self;
/// Multiplication for Baby Bear [Elem]
fn mul(self, rhs: Self) -> Self {
Elem(mul(self.0, rhs.0))
}
}

impl ops::MulAssign for Elem {
/// Simple multiplication case for Baby Bear [Elem]
fn mul_assign(&mut self, rhs: Self) {
self.0 = mul(self.0, rhs.0)
}
Expand Down Expand Up @@ -207,17 +217,20 @@ impl From<u64> for Elem {
}
}

/// Wrapping addition of [Elem] using Baby Bear field modulus
fn add(lhs: u32, rhs: u32) -> u32 {
let x = lhs.wrapping_add(rhs);
return if x >= P { x - P } else { x };
}

/// Wrapping subtraction of [Elem] using Baby Bear field modulus
fn sub(lhs: u32, rhs: u32) -> u32 {
let x = lhs.wrapping_sub(rhs);
return if x > P { x.wrapping_add(P) } else { x };
}

// Copied from C++ implementation (fp.h)
/// Wrapping multiplication of [Elem] using Baby Bear field modulus
// Copied from the C++ implementation (fp.h)
const fn mul(lhs: u32, rhs: u32) -> u32 {
// uint64_t o64 = uint64_t(a) * uint64_t(b);
let mut o64: u64 = (lhs as u64).wrapping_mul(rhs as u64);
Expand All @@ -237,27 +250,26 @@ const fn mul(lhs: u32, rhs: u32) -> u32 {
}
}

/// Encode to montgomery form from direct form.
/// Encode to Montgomery form from direct form.
const fn encode(a: u32) -> u32 {
mul(R2, a)
}

/// Decode from montgomery form from direct form.
/// Decode from Montgomery form from direct form.
const fn decode(a: u32) -> u32 {
mul(1, a)
}

/// The size of the extension field in elements, 4 in this case.
const EXT_SIZE: usize = 4;

/// Instances of `ExtElem` are elements of a finite field `F_p^4`. They are
/// represented as elements of `F_p[X] / (X^4 - 11)`. Basically, this is a *big*
/// finite field (about `2^128` elements), which is used when the security of
/// various operations depends on the size of the field. It has the field
/// `Elem` as a subfield, which means operations by the two are compatable,
/// which is important. The irreducible polynomial was choosen to be the most
/// simple possible one, `x^4 - B`, where `11` is the smallest `B` which makes
/// the polynomial irreducable.
/// represented as elements of `F_p[X] / (X^4 - 11)`. This large
/// finite field (about `2^128` elements) is used when the security of
/// operations depends on the size of the field. The field extension `ExtElem`
/// has `Elem` as a subfield, so operations on elements of each are compatible.
/// The irreducible polynomial `x^4 - 11` was chosen because `11` is
/// the simplest choice of `BETA` for `x^2 - BETA` that makes this polynomial
/// irreducible.
#[derive(Eq, PartialEq, Clone, Copy, Debug, Pod, Zeroable)]
#[repr(transparent)]
pub struct ExtElem([Elem; EXT_SIZE]);
Expand Down Expand Up @@ -359,39 +371,40 @@ impl ExtElem {
Self([x0, x1, x2, x3])
}

/// Create a [ExtElem] from a [Elem].
/// Create an [ExtElem] from an [Elem].
pub fn from_fp(x: Elem) -> Self {
Self([x, Elem::new(0), Elem::new(0), Elem::new(0)])
}

/// Create a [ExtElem] from a raw integer.
/// Create an [ExtElem] from a raw integer.
pub const fn from_u32(x0: u32) -> Self {
Self([Elem::new(x0), Elem::new(0), Elem::new(0), Elem::new(0)])
}

/// Returns the value zero.
/// Return the value zero.
const fn zero() -> Self {
Self::from_u32(0)
}

/// Returns the value one.
/// Return the value one.
const fn one() -> Self {
Self::from_u32(1)
}

/// Returns the constant portion of a [Elem].
/// Return the base field term of an [Elem].
pub fn const_part(self) -> Elem {
self.0[0]
}

/// Returns the elements of a [Elem].
/// Return [Elem] as a vector of base field values.
pub fn elems(&self) -> &[Elem] {
&self.0
}
}

impl ops::Add for ExtElem {
type Output = Self;
/// Addition for Baby Bear [ExtElem]
fn add(self, rhs: Self) -> Self {
let mut lhs = self;
lhs += rhs;
Expand All @@ -400,6 +413,7 @@ impl ops::Add for ExtElem {
}

impl ops::AddAssign for ExtElem {
/// Simple addition case for Baby Bear [ExtElem]
fn add_assign(&mut self, rhs: Self) {
for i in 0..self.0.len() {
self.0[i] += rhs.0[i];
Expand All @@ -409,6 +423,7 @@ impl ops::AddAssign for ExtElem {

impl ops::Sub for ExtElem {
type Output = Self;
/// Subtraction for Baby Bear [ExtElem]
fn sub(self, rhs: Self) -> Self {
let mut lhs = self;
lhs -= rhs;
Expand All @@ -417,15 +432,17 @@ impl ops::Sub for ExtElem {
}

impl ops::SubAssign for ExtElem {
/// Simple subtraction case for Baby Bear [ExtElem]
fn sub_assign(&mut self, rhs: Self) {
for i in 0..self.0.len() {
self.0[i] -= rhs.0[i];
}
}
}

/// Implement the simple multiplication case by the subfield Elem.
impl ops::MulAssign<Elem> for ExtElem {
/// Simple multiplication case by a
/// Baby Bear [Elem]
fn mul_assign(&mut self, rhs: Elem) {
for i in 0..self.0.len() {
self.0[i] *= rhs;
Expand All @@ -435,6 +452,7 @@ impl ops::MulAssign<Elem> for ExtElem {

impl ops::Mul<Elem> for ExtElem {
type Output = Self;
/// Multiplication by a Baby Bear [Elem]
fn mul(self, rhs: Elem) -> Self {
let mut lhs = self;
lhs *= rhs;
Expand All @@ -444,6 +462,7 @@ impl ops::Mul<Elem> for ExtElem {

impl ops::Mul<ExtElem> for Elem {
type Output = ExtElem;
/// Multiplication for a subfield [Elem] by an [ExtElem]
fn mul(self, rhs: ExtElem) -> ExtElem {
rhs * self
}
Expand Down Expand Up @@ -517,8 +536,8 @@ mod tests {
#[test]
fn isa_field() {
let mut rng = rand::rngs::SmallRng::seed_from_u64(2);
// Pick random sets of 3 elements of ExtElem, and verify they meet the
// requirements of a field.
// Generate three field extension elements using randomly generated base field
// values, and verify they meet the requirements of a field.
for _ in 0..1_000 {
let a = ExtElem::random(&mut rng);
let b = ExtElem::random(&mut rng);
Expand Down
Loading

0 comments on commit 3f2c728

Please sign in to comment.