diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index f25bce79..d2e2cac3 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -1,9 +1,21 @@ -use std::mem::transmute; use std::ops::{Add, Div, Mul, Neg, Sub}; -use ethereum_types::U512; -use rand::distributions::{Distribution, Standard}; -use rand::Rng; +use ethereum_types::{U512}; +// use rand::distributions::{Distribution, Standard}; +// use rand::Rng; + +pub trait FieldExt: + Sized + + std::ops::Add + + std::ops::Neg + + std::ops::Sub + + std::ops::Mul + + std::ops::Div +{ + const ZERO: Self; + const UNIT: Self; + fn inv(self) -> Self; +} pub const BLS_BASE: U512 = U512([ 0xb9feffffffffaaab, @@ -29,14 +41,14 @@ impl Fp { } } -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp { - let xs = rng.gen::<[u64; 8]>(); - Fp { - val: U512(xs) % BLS_BASE, - } - } -} +// impl Distribution for Standard { +// fn sample(&self, rng: &mut R) -> Fp { +// let xs = rng.gen::<[u64; 8]>(); +// Fp { +// val: U512(xs) % BLS_BASE, +// } +// } +// } impl Add for Fp { type Output = Self; @@ -113,8 +125,10 @@ impl Mul for Fp { } } -impl Fp { - pub fn inv(self) -> Fp { +impl FieldExt for Fp { + const ZERO: Self = Fp { val: U512::zero() }; + const UNIT: Self = Fp { val: U512::one() }; + fn inv(self) -> Fp { exp_fp(self, BLS_BASE - 2) } } @@ -128,9 +142,6 @@ impl Div for Fp { } } -pub const ZERO_FP: Fp = Fp { val: U512::zero() }; -pub const UNIT_FP: Fp = Fp { val: U512::one() }; - fn exp_fp(x: Fp, e: U512) -> Fp { let mut current = x; let mut product = Fp { val: U512::one() }; @@ -147,29 +158,20 @@ fn exp_fp(x: Fp, e: U512) -> Fp { /// The degree 2 field extension Fp2 is given by adjoining i, the square root of -1, to Fp /// The arithmetic in this extension is standard complex arithmetic #[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp2 { - pub re: Fp, - pub im: Fp, +pub struct Fp2 where T: FieldExt { + pub re: T, + pub im: T, } -pub const ZERO_FP2: Fp2 = Fp2 { - re: ZERO_FP, - im: ZERO_FP, -}; -pub const UNIT_FP2: Fp2 = Fp2 { - re: UNIT_FP, - im: ZERO_FP, -}; +// impl> Distribution> for Standard { +// fn sample(&self, rng: &mut R) -> Fp2 { +// let (re, im) = rng.gen::<(T, T)>(); +// Fp2 { re, im } +// } +// } -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp2 { - let (re, im) = rng.gen::<(Fp, Fp)>(); - Fp2 { re, im } - } -} - -impl Add for Fp2 { +impl Add for Fp2 { type Output = Self; fn add(self, other: Self) -> Self { @@ -180,7 +182,7 @@ impl Add for Fp2 { } } -impl Neg for Fp2 { +impl Neg for Fp2 { type Output = Self; fn neg(self) -> Self::Output { @@ -191,7 +193,7 @@ impl Neg for Fp2 { } } -impl Sub for Fp2 { +impl Sub for Fp2 { type Output = Self; fn sub(self, other: Self) -> Self { @@ -202,7 +204,9 @@ impl Sub for Fp2 { } } -impl Mul for Fp2 { +impl Mul + for Fp2 +{ type Output = Self; fn mul(self, other: Self) -> Self { @@ -213,17 +217,9 @@ impl Mul for Fp2 { } } -impl Fp2 { - // We preemptively define a helper function which multiplies an Fp2 element by 1 + i - fn i1(self) -> Fp2 { - Fp2 { - re: self.re - self.im, - im: self.re + self.im, - } - } - - // This function scalar multiplies an Fp2 by an Fp - pub fn scale(self, x: Fp) -> Fp2 { +impl Fp2 { + /// This function scalar multiplies an Fp2 by an Fp + pub fn scale(self, x: T) -> Self { Fp2 { re: x * self.re, im: x * self.im, @@ -234,8 +230,8 @@ impl Fp2 { /// This also happens to be the frobenius map /// z -> z^p /// since p == 3 mod 4 and hence - /// i^p = i^3 = -i - fn conj(self) -> Fp2 { + /// i^p = i^(4k) * i^3 = 1*(-i) = -i + fn conj(self) -> Self { Fp2 { re: self.re, im: -self.im, @@ -243,19 +239,30 @@ impl Fp2 { } // Return the magnitude squared of a complex number - fn norm_sq(self) -> Fp { + fn norm_sq(self) -> T { self.re * self.re + self.im * self.im } +} +impl FieldExt for Fp2 { + const ZERO: Fp2 = Fp2 { + re: T::ZERO, + im: T::ZERO, + }; + + const UNIT: Fp2 = Fp2 { + re: T::UNIT, + im: T::ZERO, + }; /// The inverse of z is given by z'/||z||^2 since ||z||^2 = zz' - pub fn inv(self) -> Fp2 { + fn inv(self) -> Fp2 { let norm_sq = self.norm_sq(); self.conj().scale(norm_sq.inv()) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp2 { +impl Div for Fp2 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -263,35 +270,40 @@ impl Div for Fp2 { } } -/// The degree 3 field extension Fp6 over Fp2 is given by adjoining t, where t^3 = 1 + i -// Fp6 has basis 1, t, t^2 over Fp2 -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp6 { - pub t0: Fp2, - pub t1: Fp2, - pub t2: Fp2, +trait Adj { + fn mul_adj(self) -> Self; } -pub const ZERO_FP6: Fp6 = Fp6 { - t0: ZERO_FP2, - t1: ZERO_FP2, - t2: ZERO_FP2, -}; - -pub const UNIT_FP6: Fp6 = Fp6 { - t0: UNIT_FP2, - t1: ZERO_FP2, - t2: ZERO_FP2, -}; - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp6 { - let (t0, t1, t2) = rng.gen::<(Fp2, Fp2, Fp2)>(); - Fp6 { t0, t1, t2 } +/// Helper function which multiplies by the Fp2 element +/// whose cube root we will adjoin in the next extension +impl Adj for Fp2 { + fn mul_adj(self) -> Self { + Fp2 { + re: self.re - self.im, + im: self.re + self.im, + } } } -impl Add for Fp6 { +/// The degree 3 field extension Fp6 over Fp2 is given by adjoining t, where t^3 = 1 + i +/// Fp6 has basis 1, t, t^2 over Fp2 +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Fp6 where T: FieldExt { + pub t0: Fp2, + pub t1: Fp2, + pub t2: Fp2, +} + + + +// impl> Distribution> for Standard { +// fn sample(&self, rng: &mut R) -> Fp6 { +// let (t0, t1, t2) = rng.gen::<(Fp2, Fp2, Fp2)>(); +// Fp6 { t0, t1, t2 } +// } +// } + +impl Add for Fp6 { type Output = Self; fn add(self, other: Self) -> Self { @@ -303,7 +315,7 @@ impl Add for Fp6 { } } -impl Neg for Fp6 { +impl Neg for Fp6 { type Output = Self; fn neg(self) -> Self::Output { @@ -315,7 +327,7 @@ impl Neg for Fp6 { } } -impl Sub for Fp6 { +impl Sub for Fp6 { type Output = Self; fn sub(self, other: Self) -> Self { @@ -327,38 +339,47 @@ impl Sub for Fp6 { } } -impl Mul for Fp6 { +impl Mul for Fp6 { type Output = Self; fn mul(self, other: Self) -> Self { Fp6 { - t0: self.t0 * other.t0 + (self.t1 * other.t2 + self.t2 * other.t1).i1(), - t1: self.t0 * other.t1 + self.t1 * other.t0 + (self.t2 * other.t2).i1(), + t0: self.t0 * other.t0 + (self.t1 * other.t2 + self.t2 * other.t1).mul_adj(), + t1: self.t0 * other.t1 + self.t1 * other.t0 + (self.t2 * other.t2).mul_adj(), t2: self.t0 * other.t2 + self.t1 * other.t1 + self.t2 * other.t0, } } } -impl Fp6 { +impl Fp6 { // This function scalar multiplies an Fp6 by an Fp2 - fn scale(self, x: Fp2) -> Fp6 { + fn scale(self, x: Fp2) -> Fp6 { Fp6 { t0: x * self.t0, t1: x * self.t1, t2: x * self.t2, } } +} +impl Fp6 { /// This function multiplies an Fp6 element by t, and hence shifts the bases, /// where the t^2 coefficient picks up a factor of 1+i as the 1 coefficient of the output - fn sh(self) -> Fp6 { + fn sh(self) -> Fp6 { Fp6 { - t0: self.t2.i1(), + t0: self.t2.mul_adj(), t1: self.t0, t2: self.t1, } } +} +pub trait Frob { + const FROB_T: Self; + const FROB_Z: Self; +} + +impl Fp6 { /// The nth frobenius endomorphism of a p^q field is given by mapping /// x to x^(p^n) /// which sends a + bt + ct^2: Fp6 to @@ -367,10 +388,10 @@ impl Fp6 { /// while the values of /// t^(p^n) and t^(2p^n) /// are precomputed in the constant arrays FROB_T1 and FROB_T2 - pub fn frob(self, n: usize) -> Fp6 { + pub fn frob(self, n: usize) -> Fp6 { let n = n % 6; - let frob_t1 = FROB_T1[n]; - let frob_t2 = FROB_T2[n]; + let frob_t1 = Self::FROB_T[0][n]; + let frob_t2 = Self::FROB_T[1][n]; if n % 2 != 0 { Fp6 { @@ -386,6 +407,20 @@ impl Fp6 { } } } +} + +impl FieldExt for Fp6 { + const ZERO: Fp6 = Fp6 { + t0: Fp2::::ZERO, + t1: Fp2::::ZERO, + t2: Fp2::::ZERO, + }; + + const UNIT: Fp6 = Fp6 { + t0: Fp2::::UNIT, + t1: Fp2::::ZERO, + t2: Fp2::::ZERO, + }; /// Let x_n = x^(p^n) and note that /// x_0 = x^(p^0) = x^1 = x @@ -402,7 +437,7 @@ impl Fp6 { /// phi = ||x_1 * x_3 * x_5||^2 /// and hence the inverse is given by /// ([x_1 * x_3] * x_5) * [x_1 * x_3]_1 / ||[x_1 * x_3] * x_5||^2 - pub fn inv(self) -> Fp6 { + fn inv(self) -> Fp6 { let prod_13 = self.frob(1) * self.frob(3); let prod_135 = (prod_13 * self.frob(5)).t0; let phi = prod_135.norm_sq(); @@ -410,15 +445,10 @@ impl Fp6 { let prod_24 = prod_13.frob(1); prod_24.scale(prod_odds_over_phi) } - - pub fn on_stack(self) -> Vec { - let f: [U512; 6] = unsafe { transmute(self) }; - f.into_iter().collect() - } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp6 { +impl Div for Fp6 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -426,108 +456,111 @@ impl Div for Fp6 { } } -/// The degree 2 field extension Fp12 over Fp6 is given by adjoining z, where z^2 = t. -/// It thus has basis 1, z over Fp6 -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp12 { - pub z0: Fp6, - pub z1: Fp6, -} +// /// The degree 2 field extension Fp12 over Fp6 is given by adjoining z, where z^2 = t. +// /// It thus has basis 1, z over Fp6 +// #[derive(Debug, Copy, Clone, PartialEq)] +// pub struct Fp12 { +// pub z0: Fp6, +// pub z1: Fp6, +// } -pub const UNIT_FP12: Fp12 = Fp12 { - z0: UNIT_FP6, - z1: ZERO_FP6, -}; +// impl Unital for Fp12 { +// const ZERO: Fp12 = Fp12 { +// z0: Fp6::::ZERO, +// z1: Fp6::::ZERO, +// }; -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp12 { - let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); - Fp12 { z0, z1 } - } -} +// const UNIT: Fp12 = Fp12 { +// z0: Fp6::::UNIT, +// z1: Fp6::::ZERO, +// }; +// } -impl Mul for Fp12 { - type Output = Self; +// // impl> Distribution> for Standard { +// // fn sample(&self, rng: &mut R) -> Fp12 { +// // let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); +// // Fp12 { z0, z1 } +// // } +// // } - fn mul(self, other: Self) -> Self { - let h0 = self.z0 * other.z0; - let h1 = self.z1 * other.z1; - let h01 = (self.z0 + self.z1) * (other.z0 + other.z1); - Fp12 { - z0: h0 + h1.sh(), - z1: h01 - (h0 + h1), - } - } -} +// impl Mul for Fp12 { +// type Output = Self; -impl Fp12 { - // This function scalar multiplies an Fp12 by an Fp6 - fn scale(self, x: Fp6) -> Fp12 { - Fp12 { - z0: x * self.z0, - z1: x * self.z1, - } - } +// fn mul(self, other: Self) -> Self { +// let h0 = self.z0 * other.z0; +// let h1 = self.z1 * other.z1; +// let h01 = (self.z0 + self.z1) * (other.z0 + other.z1); +// Fp12 { +// z0: h0 + h1.sh(), +// z1: h01 - (h0 + h1), +// } +// } +// } - fn conj(self) -> Fp12 { - Fp12 { - z0: self.z0, - z1: -self.z1, - } - } - /// The nth frobenius endomorphism of a p^q field is given by mapping - /// x to x^(p^n) - /// which sends a + bz: Fp12 to - /// a^(p^n) + b^(p^n) * z^(p^n) - /// where the values of z^(p^n) are precomputed in the constant array FROB_Z - pub fn frob(self, n: usize) -> Fp12 { - let n = n % 12; - Fp12 { - z0: self.z0.frob(n), - z1: self.z1.frob(n).scale(FROB_Z[n]), - } - } +// impl Fp12 { +// // This function scalar multiplies an Fp12 by an Fp6 +// fn scale(self, x: Fp6) -> Fp12 { +// Fp12 { +// z0: x * self.z0, +// z1: x * self.z1, +// } +// } - /// By Galois Theory, given x: Fp12, the product - /// phi = Prod_{i=0}^11 x_i - /// lands in Fp, and hence the inverse of x is given by - /// (Prod_{i=1}^11 x_i) / phi - /// The 6th Frob map is nontrivial but leaves Fp6 fixed and hence must be the conjugate: - /// x_6 = (a + bz)_6 = a - bz = x.conj() - /// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expresed as: - /// [(prod_17) * (prod_17)_2] * (prod_17)_4 * [(prod_17) * (prod_17)_2]_1 - /// By Galois theory, both the following are in Fp2 and are complex conjugates - /// prod_odds, prod_evens - /// Thus phi = ||prod_odds||^2, and hence the inverse is given by - /// prod_odds * prod_evens_except_six * x.conj() / ||prod_odds||^2 - pub fn inv(self) -> Fp12 { - let prod_17 = (self.frob(1) * self.frob(7)).z0; - let prod_1379 = prod_17 * prod_17.frob(2); - let prod_odds = (prod_1379 * prod_17.frob(4)).t0; - let phi = prod_odds.norm_sq(); - let prod_odds_over_phi = prod_odds.scale(phi.inv()); - let prod_evens_except_six = prod_1379.frob(1); - let prod_except_six = prod_evens_except_six.scale(prod_odds_over_phi); - self.conj().scale(prod_except_six) - } +// fn conj(self) -> Fp12 { +// Fp12 { +// z0: self.z0, +// z1: -self.z1, +// } +// } +// } - pub fn on_stack(self) -> Vec { - let f: [U512; 12] = unsafe { transmute(self) }; - f.into_iter().collect() - } -} +// impl Fp12 { +// /// The nth frobenius endomorphism of a p^q field is given by mapping +// /// x to x^(p^n) +// /// which sends a + bz: Fp12 to +// /// a^(p^n) + b^(p^n) * z^(p^n) +// /// where the values of z^(p^n) are precomputed in the constant array FROB_Z +// pub fn frob(self, n: usize) -> Fp12 { +// let n = n % 12; +// Fp12 { +// z0: self.z0.frob(n), +// z1: self.z1.frob(n).scale(Self::FROB_Z[n]), +// } +// } -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp12 { - type Output = Self; +// /// By Galois Theory, given x: Fp12, the product +// /// phi = Prod_{i=0}^11 x_i +// /// lands in Fp, and hence the inverse of x is given by +// /// (Prod_{i=1}^11 x_i) / phi +// /// The 6th Frob map is nontrivial but leaves Fp6 fixed and hence must be the conjugate: +// /// x_6 = (a + bz)_6 = a - bz = x.conj() +// /// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expresed as: +// /// [(prod_17) * (prod_17)_2] * (prod_17)_4 * [(prod_17) * (prod_17)_2]_1 +// /// By Galois theory, both the following are in Fp2 and are complex conjugates +// /// prod_odds, prod_evens +// /// Thus phi = ||prod_odds||^2, and hence the inverse is given by +// /// prod_odds * prod_evens_except_six * x.conj() / ||prod_odds||^2 +// pub fn inv(self) -> Fp12 { +// let prod_17 = (self.frob(1) * self.frob(7)).z0; +// let prod_1379 = prod_17 * prod_17.frob(2); +// let prod_odds = (prod_1379 * prod_17.frob(4)).t0; +// let phi = prod_odds.norm_sq(); +// let prod_odds_over_phi = prod_odds.scale(phi.inv()); +// let prod_evens_except_six = prod_1379.frob(1); +// let prod_except_six = prod_evens_except_six.scale(prod_odds_over_phi); +// self.conj().scale(prod_except_six) +// } +// } - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} +// #[allow(clippy::suspicious_arithmetic_impl)] +// impl> Div for Fp12 { +// type Output = Self; -const FROB_T1: [Fp2; 6] = [ZERO_FP2; 6]; +// fn div(self, rhs: Self) -> Self::Output { +// self * rhs.inv() +// } +// } -const FROB_T2: [Fp2; 6] = [ZERO_FP2; 6]; - -const FROB_Z: [Fp2; 12] = [ZERO_FP2; 12]; +// trait Stack { +// fn on_stack(self) -> Vec; +// }