diff --git a/evm/src/arithmetic/mod.rs b/evm/src/arithmetic/mod.rs index 8d9592b7..c6987ed7 100644 --- a/evm/src/arithmetic/mod.rs +++ b/evm/src/arithmetic/mod.rs @@ -1,7 +1,7 @@ use ethereum_types::U256; use plonky2::field::types::PrimeField64; -use crate::bn254_arithmetic::BN_BASE; +use crate::extension_tower::BN_BASE; use crate::util::{addmod, mulmod, submod}; mod addcy; diff --git a/evm/src/bn254_arithmetic.rs b/evm/src/bn254_arithmetic.rs deleted file mode 100644 index c2f1e3d4..00000000 --- a/evm/src/bn254_arithmetic.rs +++ /dev/null @@ -1,876 +0,0 @@ -use std::mem::transmute; -use std::ops::{Add, Div, Mul, Neg, Sub}; - -use ethereum_types::U256; -use rand::distributions::{Distribution, Standard}; -use rand::Rng; - -pub const BN_BASE: U256 = U256([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, -]); - -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp { - pub val: U256, -} - -impl Fp { - pub fn new(val: usize) -> Fp { - Fp { - val: U256::from(val), - } - } -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp { - let xs = rng.gen::<[u64; 4]>(); - Fp { - val: U256(xs) % BN_BASE, - } - } -} - -impl Add for Fp { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp { - val: (self.val + other.val) % BN_BASE, - } - } -} - -impl Neg for Fp { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp { - val: (BN_BASE - self.val) % BN_BASE, - } - } -} - -impl Sub for Fp { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp { - val: (BN_BASE + self.val - other.val) % BN_BASE, - } - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for Fp { - type Output = Self; - - fn mul(self, other: Self) -> Self { - Fp { - val: U256::try_from((self.val).full_mul(other.val) % BN_BASE).unwrap(), - } - } -} - -impl Fp { - pub fn inv(self) -> Fp { - exp_fp(self, BN_BASE - 2) - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -pub const ZERO_FP: Fp = Fp { val: U256::zero() }; -pub const UNIT_FP: Fp = Fp { val: U256::one() }; - -fn exp_fp(x: Fp, e: U256) -> Fp { - let mut current = x; - let mut product = Fp { val: U256::one() }; - - for j in 0..256 { - if e.bit(j) { - product = product * current; - } - current = current * current; - } - product -} - -/// 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 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::<(Fp, Fp)>(); - Fp2 { re, im } - } -} - -impl Add for Fp2 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp2 { - re: self.re + other.re, - im: self.im + other.im, - } - } -} - -impl Neg for Fp2 { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp2 { - re: -self.re, - im: -self.im, - } - } -} - -impl Sub for Fp2 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp2 { - re: self.re - other.re, - im: self.im - other.im, - } - } -} - -impl Mul for Fp2 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - Fp2 { - re: self.re * other.re - self.im * other.im, - im: self.re * other.im + self.im * other.re, - } - } -} - -impl Fp2 { - // We preemptively define a helper function which multiplies an Fp2 element by 9 + i - fn i9(self) -> Fp2 { - let nine = Fp::new(9); - Fp2 { - re: nine * self.re - self.im, - im: self.re + nine * self.im, - } - } - - // This function scalar multiplies an Fp2 by an Fp - pub fn scale(self, x: Fp) -> Fp2 { - Fp2 { - re: x * self.re, - im: x * self.im, - } - } - - /// Return the complex conjugate z' of z: 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 { - Fp2 { - re: self.re, - im: -self.im, - } - } - - // Return the magnitude squared of a complex number - fn norm_sq(self) -> Fp { - self.re * self.re + self.im * self.im - } - - /// The inverse of z is given by z'/||z||^2 since ||z||^2 = zz' - pub 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 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -/// The degree 3 field extension Fp6 over Fp2 is given by adjoining t, where t^3 = 9 + 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, -} - -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 } - } -} - -impl Add for Fp6 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp6 { - t0: self.t0 + other.t0, - t1: self.t1 + other.t1, - t2: self.t2 + other.t2, - } - } -} - -impl Neg for Fp6 { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp6 { - t0: -self.t0, - t1: -self.t1, - t2: -self.t2, - } - } -} - -impl Sub for Fp6 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp6 { - t0: self.t0 - other.t0, - t1: self.t1 - other.t1, - t2: self.t2 - other.t2, - } - } -} - -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).i9(), - t1: self.t0 * other.t1 + self.t1 * other.t0 + (self.t2 * other.t2).i9(), - t2: self.t0 * other.t2 + self.t1 * other.t1 + self.t2 * other.t0, - } - } -} - -impl Fp6 { - // This function scalar multiplies an Fp6 by an Fp2 - fn scale(self, x: Fp2) -> Fp6 { - Fp6 { - t0: x * self.t0, - t1: x * self.t1, - t2: x * self.t2, - } - } - - /// This function multiplies an Fp6 element by t, and hence shifts the bases, - /// where the t^2 coefficient picks up a factor of 9+i as the 1 coefficient of the output - fn sh(self) -> Fp6 { - Fp6 { - t0: self.t2.i9(), - t1: self.t0, - t2: self.t1, - } - } - - /// 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 - /// a^(p^n) + b^(p^n) * t^(p^n) + c^(p^n) * t^(2p^n) - /// The Fp2 coefficients are determined by the comment in the conj method, - /// 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 { - let n = n % 6; - let frob_t1 = FROB_T1[n]; - let frob_t2 = FROB_T2[n]; - - if n % 2 != 0 { - Fp6 { - t0: self.t0.conj(), - t1: frob_t1 * self.t1.conj(), - t2: frob_t2 * self.t2.conj(), - } - } else { - Fp6 { - t0: self.t0, - t1: frob_t1 * self.t1, - t2: frob_t2 * self.t2, - } - } - } - - /// Let x_n = x^(p^n) and note that - /// x_0 = x^(p^0) = x^1 = x - /// (x_n)_m = (x^(p^n))^(p^m) = x^(p^n * p^m) = x^(p^(n+m)) = x_{n+m} - /// By Galois Theory, given x: Fp6, the product - /// phi = x_0 * x_1 * x_2 * x_3 * x_4 * x_5 - /// lands in Fp, and hence the inverse of x is given by - /// (x_1 * x_2 * x_3 * x_4 * x_5) / phi - /// We can save compute by rearranging the numerator: - /// (x_1 * x_3) * x_5 * (x_1 * x_3)_1 - /// By Galois theory, the following are in Fp2 and are complex conjugates - /// x_1 * x_3 * x_5, x_0 * x_2 * x_4 - /// and therefore - /// 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 { - let prod_13 = self.frob(1) * self.frob(3); - let prod_135 = (prod_13 * self.frob(5)).t0; - let phi = prod_135.norm_sq(); - let prod_odds_over_phi = prod_135.scale(phi.inv()); - let prod_24 = prod_13.frob(1); - prod_24.scale(prod_odds_over_phi) - } - - pub fn on_stack(self) -> Vec { - let f: [U256; 6] = unsafe { transmute(self) }; - f.into_iter().collect() - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp6 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -/// 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 Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp12 { - let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); - Fp12 { z0, z1 } - } -} - -impl Mul for Fp12 { - type Output = Self; - - 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 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 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]), - } - } - - /// 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) - } - - pub fn on_stack(self) -> Vec { - let f: [U256; 12] = unsafe { transmute(self) }; - f.into_iter().collect() - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp12 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -const FROB_T1: [Fp2; 6] = [ - Fp2 { - re: Fp { val: U256::one() }, - im: Fp { val: U256::zero() }, - }, - Fp2 { - re: Fp { - val: U256([ - 0x99e39557176f553d, - 0xb78cc310c2c3330c, - 0x4c0bec3cf559b143, - 0x2fb347984f7911f7, - ]), - }, - im: Fp { - val: U256([ - 0x1665d51c640fcba2, - 0x32ae2a1d0b7c9dce, - 0x4ba4cc8bd75a0794, - 0x16c9e55061ebae20, - ]), - }, - }, - Fp2 { - re: Fp { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - }, - im: Fp { val: U256::zero() }, - }, - Fp2 { - re: Fp { - val: U256([ - 0x7b746ee87bdcfb6d, - 0x805ffd3d5d6942d3, - 0xbaff1c77959f25ac, - 0x0856e078b755ef0a, - ]), - }, - im: Fp { - val: U256([ - 0x380cab2baaa586de, - 0x0fdf31bf98ff2631, - 0xa9f30e6dec26094f, - 0x04f1de41b3d1766f, - ]), - }, - }, - Fp2 { - re: Fp { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - }, - im: Fp { val: U256::zero() }, - }, - Fp2 { - re: Fp { - val: U256([ - 0x62e913ee1dada9e4, - 0xf71614d4b0b71f3a, - 0x699582b87809d9ca, - 0x28be74d4bb943f51, - ]), - }, - im: Fp { - val: U256([ - 0xedae0bcec9c7aac7, - 0x54f40eb4c3f6068d, - 0xc2b86abcbe01477a, - 0x14a88ae0cb747b99, - ]), - }, - }, -]; - -const FROB_T2: [Fp2; 6] = [ - Fp2 { - re: Fp { val: U256::one() }, - im: Fp { val: U256::zero() }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x848a1f55921ea762, - 0xd33365f7be94ec72, - 0x80f3c0b75a181e84, - 0x05b54f5e64eea801, - ]), - } - }, - im: { - Fp { - val: U256([ - 0xc13b4711cd2b8126, - 0x3685d2ea1bdec763, - 0x9f3a80b03b0b1c92, - 0x2c145edbe7fd8aee, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x0e1a92bc3ccbf066, - 0xe633094575b06bcb, - 0x19bee0f7b5b2444e, - 0xbc58c6611c08dab, - ]), - } - }, - im: { - Fp { - val: U256([ - 0x5fe3ed9d730c239f, - 0xa44a9e08737f96e5, - 0xfeb0f6ef0cd21d04, - 0x23d5e999e1910a12, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xa97bda050992657f, - 0xde1afb54342c724f, - 0x1d9da40771b6f589, - 0x1ee972ae6a826a7d, - ]), - } - }, - im: { - Fp { - val: U256([ - 0x5721e37e70c255c9, - 0x54326430418536d1, - 0xd2b513cdbb257724, - 0x10de546ff8d4ab51, - ]), - } - }, - }, -]; - -const FROB_Z: [Fp2; 12] = [ - Fp2 { - re: { Fp { val: U256::one() } }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xd60b35dadcc9e470, - 0x5c521e08292f2176, - 0xe8b99fdd76e68b60, - 0x1284b71c2865a7df, - ]), - } - }, - im: { - Fp { - val: U256([ - 0xca5cf05f80f362ac, - 0x747992778eeec7e5, - 0xa6327cfe12150b8e, - 0x246996f3b4fae7e6, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xe4bd44e5607cfd49, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xe86f7d391ed4a67f, - 0x894cb38dbe55d24a, - 0xefe9608cd0acaa90, - 0x19dc81cfcc82e4bb, - ]), - } - }, - im: { - Fp { - val: U256([ - 0x7694aa2bf4c0c101, - 0x7f03a5e397d439ec, - 0x06cbeee33576139d, - 0xabf8b60be77d73, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x1264475e420ac20f, - 0x2cfa95859526b0d4, - 0x072fc0af59c61f30, - 0x757cab3a41d3cdc, - ]), - } - }, - im: { - Fp { - val: U256([ - 0xe85845e34c4a5b9c, - 0xa20b7dfd71573c93, - 0x18e9b79ba4e2606c, - 0xca6b035381e35b6, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x3c208c16d87cfd46, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x6615563bfbb318d7, - 0x3b2f4c893f42a916, - 0xcf96a5d90a9accfd, - 0x1ddf9756b8cbf849, - ]), - } - }, - im: { - Fp { - val: U256([ - 0x71c39bb757899a9b, - 0x2307d819d98302a7, - 0x121dc8b86f6c4ccf, - 0x0bfab77f2c36b843, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x53b10eddb9a856c8, - 0x0e34b703aa1bf842, - 0xc866e529b0d4adcd, - 0x1687cca314aebb6d, - ]), - } - }, - im: { - Fp { - val: U256([ - 0xc58be1eae3bc3c46, - 0x187dc4add09d90a0, - 0xb18456d34c0b44c0, - 0x2fb855bcd54a22b6, - ]), - } - }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x5763473177ffffff, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { Fp { val: U256::zero() } }, - }, - Fp2 { - re: { - Fp { - val: U256([ - 0x29bc44b896723b38, - 0x6a86d50bd34b19b9, - 0xb120850727bb392d, - 0x290c83bf3d14634d, - ]), - } - }, - im: { - Fp { - val: U256([ - 0x53c846338c32a1ab, - 0xf575ec93f71a8df9, - 0x9f668e1adc9ef7f0, - 0x23bd9e3da9136a73, - ]), - } - }, - }, -]; diff --git a/evm/src/bn254_pairing.rs b/evm/src/bn254_pairing.rs index bf5db74a..7277c2a8 100644 --- a/evm/src/bn254_pairing.rs +++ b/evm/src/bn254_pairing.rs @@ -2,13 +2,13 @@ use std::ops::Add; use rand::Rng; -use crate::bn254_arithmetic::{Fp, Fp12, Fp2, Fp6, UNIT_FP12, ZERO_FP, ZERO_FP2}; +use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, BN254}; -// The curve consists of pairs (x, y): (Fp, Fp) | y^2 = x^3 + 2 +// The curve consists of pairs (x, y): (BN254, BN254) | y^2 = x^3 + 2 #[derive(Debug, Copy, Clone, PartialEq)] pub struct Curve { - pub x: Fp, - pub y: Fp, + pub x: BN254, + pub y: BN254, } /// Standard addition formula for elliptic curves, restricted to the cases @@ -19,7 +19,7 @@ impl Add for Curve { fn add(self, other: Self) -> Self { let m = if self == other { - Fp::new(3) * self.x * self.x / (Fp::new(2) * self.y) + BN254::new(3) * self.x * self.x / (BN254::new(2) * self.y) } else { (other.y - self.y) / (other.x - self.x) }; @@ -34,12 +34,12 @@ impl Add for Curve { // The twisted curve consists of pairs (x, y): (Fp2, Fp2) | y^2 = x^3 + 3/(9 + i) #[derive(Debug, Copy, Clone, PartialEq)] pub struct TwistedCurve { - pub x: Fp2, - pub y: Fp2, + pub x: Fp2, + pub y: Fp2, } // The tate pairing takes a point each from the curve and its twist and outputs an Fp12 element -pub fn tate(p: Curve, q: TwistedCurve) -> Fp12 { +pub fn tate(p: Curve, q: TwistedCurve) -> Fp12 { let miller_output = miller_loop(p, q); invariant_exponent(miller_output) } @@ -47,10 +47,10 @@ pub fn tate(p: Curve, q: TwistedCurve) -> Fp12 { /// Standard code for miller loop, can be found on page 99 at this url: /// https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf#page=107 /// where EXP is a hardcoding of the array of Booleans that the loop traverses -pub fn miller_loop(p: Curve, q: TwistedCurve) -> Fp12 { +pub fn miller_loop(p: Curve, q: TwistedCurve) -> Fp12 { let mut r = p; - let mut acc = UNIT_FP12; - let mut line; + let mut acc: Fp12 = Fp12::::UNIT; + let mut line: Fp12; for i in EXP { line = tangent(r, q); @@ -66,42 +66,46 @@ pub fn miller_loop(p: Curve, q: TwistedCurve) -> Fp12 { } /// The sloped line function for doubling a point -pub fn tangent(p: Curve, q: TwistedCurve) -> Fp12 { - let cx = -Fp::new(3) * p.x * p.x; - let cy = Fp::new(2) * p.y; - sparse_embed(p.y * p.y - Fp::new(9), q.x.scale(cx), q.y.scale(cy)) +pub fn tangent(p: Curve, q: TwistedCurve) -> Fp12 { + let cx = -BN254::new(3) * p.x * p.x; + let cy = BN254::new(2) * p.y; + sparse_embed(p.y * p.y - BN254::new(9), q.x * cx, q.y * cy) } /// The sloped line function for adding two points -pub fn cord(p1: Curve, p2: Curve, q: TwistedCurve) -> Fp12 { +pub fn cord(p1: Curve, p2: Curve, q: TwistedCurve) -> Fp12 { let cx = p2.y - p1.y; let cy = p1.x - p2.x; - sparse_embed(p1.y * p2.x - p2.y * p1.x, q.x.scale(cx), q.y.scale(cy)) + sparse_embed(p1.y * p2.x - p2.y * p1.x, q.x * cx, q.y * cy) } /// The tangent and cord functions output sparse Fp12 elements. /// This map embeds the nonzero coefficients into an Fp12. -pub fn sparse_embed(g000: Fp, g01: Fp2, g11: Fp2) -> Fp12 { +pub fn sparse_embed(g000: BN254, g01: Fp2, g11: Fp2) -> Fp12 { let g0 = Fp6 { t0: Fp2 { re: g000, - im: ZERO_FP, + im: BN254::ZERO, }, t1: g01, - t2: ZERO_FP2, + t2: Fp2::::ZERO, }; let g1 = Fp6 { - t0: ZERO_FP2, + t0: Fp2::::ZERO, t1: g11, - t2: ZERO_FP2, + t2: Fp2::::ZERO, }; Fp12 { z0: g0, z1: g1 } } -pub fn gen_fp12_sparse(rng: &mut R) -> Fp12 { - sparse_embed(rng.gen::(), rng.gen::(), rng.gen::()) +pub fn gen_fp12_sparse(rng: &mut R) -> Fp12 { + sparse_embed( + rng.gen::(), + rng.gen::>(), + rng.gen::>(), + ) } /// The output y of the miller loop is not an invariant, @@ -116,7 +120,7 @@ pub fn gen_fp12_sparse(rng: &mut R) -> Fp12 { /// (p^4 - p^2 + 1)/N = p^3 + (a2)p^2 - (a1)p - a0 /// where 0 < a0, a1, a2 < p. Then the final power is given by /// y = y_3 * (y^a2)_2 * (y^-a1)_1 * (y^-a0) -pub fn invariant_exponent(f: Fp12) -> Fp12 { +pub fn invariant_exponent(f: Fp12) -> Fp12 { let mut y = f.frob(6) / f; y = y.frob(2) * y; let (y_a2, y_a1, y_a0) = get_custom_powers(y); @@ -134,11 +138,11 @@ pub fn invariant_exponent(f: Fp12) -> Fp12 { /// EXPS4 = [(a4[i], a2[i], a0[i]) for i in 0..len(a4)] /// EXPS2 = [ (a2[i], a0[i]) for i in len(a4)..len(a2)] /// EXPS0 = [ a0[i] for i in len(a2)..len(a0)] -fn get_custom_powers(f: Fp12) -> (Fp12, Fp12, Fp12) { - let mut sq: Fp12 = f; - let mut y0: Fp12 = UNIT_FP12; - let mut y2: Fp12 = UNIT_FP12; - let mut y4: Fp12 = UNIT_FP12; +fn get_custom_powers(f: Fp12) -> (Fp12, Fp12, Fp12) { + let mut sq: Fp12 = f; + let mut y0: Fp12 = Fp12::::UNIT; + let mut y2: Fp12 = Fp12::::UNIT; + let mut y4: Fp12 = Fp12::::UNIT; // proceed via standard squaring algorithm for exponentiation diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 1693f490..408671ba 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -8,11 +8,11 @@ use ethereum_types::{U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; -use crate::bn254_arithmetic::BN_BASE; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; +use crate::extension_tower::BN_BASE; use crate::generation::prover_input::ProverInputFn; use crate::generation::state::GenerationState; use crate::generation::GenerationInputs; diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs index badfb472..8e71ffd6 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -2,13 +2,13 @@ use anyhow::Result; use ethereum_types::U256; use rand::Rng; -use crate::bn254_arithmetic::{Fp, Fp12, Fp2, Fp6}; use crate::bn254_pairing::{ gen_fp12_sparse, invariant_exponent, miller_loop, tate, Curve, TwistedCurve, }; use crate::cpu::kernel::interpreter::{ run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, }; +use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254}; use crate::memory::segments::Segment::BnPairing; fn extract_stack(interpreter: Interpreter<'static>) -> Vec { @@ -20,7 +20,11 @@ fn extract_stack(interpreter: Interpreter<'static>) -> Vec { .collect::>() } -fn setup_mul_fp6_test(f: Fp6, g: Fp6, label: &str) -> InterpreterMemoryInitialization { +fn setup_mul_fp6_test( + f: Fp6, + g: Fp6, + label: &str, +) -> InterpreterMemoryInitialization { let mut stack = f.on_stack(); if label == "mul_fp254_6" { stack.extend(g.on_stack()); @@ -37,8 +41,8 @@ fn setup_mul_fp6_test(f: Fp6, g: Fp6, label: &str) -> InterpreterMemoryInitializ #[test] fn test_mul_fp6() -> Result<()> { let mut rng = rand::thread_rng(); - let f: Fp6 = rng.gen::(); - let g: Fp6 = rng.gen::(); + let f: Fp6 = rng.gen::>(); + let g: Fp6 = rng.gen::>(); let setup_normal: InterpreterMemoryInitialization = setup_mul_fp6_test(f, g, "mul_fp254_6"); let setup_square: InterpreterMemoryInitialization = setup_mul_fp6_test(f, f, "square_fp254_6"); @@ -60,8 +64,8 @@ fn test_mul_fp6() -> Result<()> { fn setup_mul_fp12_test( out: usize, - f: Fp12, - g: Fp12, + f: Fp12, + g: Fp12, label: &str, ) -> InterpreterMemoryInitialization { let in0: usize = 200; @@ -89,9 +93,9 @@ fn test_mul_fp12() -> Result<()> { let out: usize = 224; let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); - let g: Fp12 = rng.gen::(); - let h: Fp12 = gen_fp12_sparse(&mut rng); + let f: Fp12 = rng.gen::>(); + let g: Fp12 = rng.gen::>(); + let h: Fp12 = gen_fp12_sparse(&mut rng); let setup_normal: InterpreterMemoryInitialization = setup_mul_fp12_test(out, f, g, "mul_fp254_12"); @@ -119,7 +123,7 @@ fn test_mul_fp12() -> Result<()> { Ok(()) } -fn setup_frob_fp6_test(f: Fp6, n: usize) -> InterpreterMemoryInitialization { +fn setup_frob_fp6_test(f: Fp6, n: usize) -> InterpreterMemoryInitialization { InterpreterMemoryInitialization { label: String::from("test_frob_fp254_6_") + &(n.to_string()), stack: f.on_stack(), @@ -131,7 +135,7 @@ fn setup_frob_fp6_test(f: Fp6, n: usize) -> InterpreterMemoryInitialization { #[test] fn test_frob_fp6() -> Result<()> { let mut rng = rand::thread_rng(); - let f: Fp6 = rng.gen::(); + let f: Fp6 = rng.gen::>(); for n in 1..4 { let setup_frob = setup_frob_fp6_test(f, n); let intrptr_frob: Interpreter = run_interpreter_with_memory(setup_frob).unwrap(); @@ -142,7 +146,7 @@ fn test_frob_fp6() -> Result<()> { Ok(()) } -fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryInitialization { +fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryInitialization { InterpreterMemoryInitialization { label: String::from("test_frob_fp254_12_") + &(n.to_string()), stack: vec![U256::from(ptr)], @@ -155,7 +159,7 @@ fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryIniti fn test_frob_fp12() -> Result<()> { let ptr: usize = 200; let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); + let f: Fp12 = rng.gen::>(); for n in [1, 2, 3, 6] { let setup_frob = setup_frob_fp12_test(ptr, f, n); let intrptr_frob: Interpreter = run_interpreter_with_memory(setup_frob).unwrap(); @@ -171,7 +175,7 @@ fn test_inv_fp12() -> Result<()> { let ptr: usize = 200; let inv: usize = 212; let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); + let f: Fp12 = rng.gen::>(); let setup = InterpreterMemoryInitialization { label: "inv_fp254_12".to_string(), @@ -192,7 +196,7 @@ fn test_inv_fp12() -> Result<()> { fn test_invariant_exponent() -> Result<()> { let ptr: usize = 200; let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); + let f: Fp12 = rng.gen::>(); let setup = InterpreterMemoryInitialization { label: "bn254_invariant_exponent".to_string(), @@ -213,8 +217,8 @@ fn test_invariant_exponent() -> Result<()> { // The curve is cyclic with generator (1, 2) pub const CURVE_GENERATOR: Curve = { Curve { - x: Fp { val: U256::one() }, - y: Fp { + x: BN254 { val: U256::one() }, + y: BN254 { val: U256([2, 0, 0, 0]), }, } @@ -224,7 +228,7 @@ pub const CURVE_GENERATOR: Curve = { pub const TWISTED_GENERATOR: TwistedCurve = { TwistedCurve { x: Fp2 { - re: Fp { + re: BN254 { val: U256([ 0x46debd5cd992f6ed, 0x674322d4f75edadd, @@ -232,7 +236,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = { 0x1800deef121f1e76, ]), }, - im: Fp { + im: BN254 { val: U256([ 0x97e485b7aef312c2, 0xf1aa493335a9e712, @@ -242,7 +246,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = { }, }, y: Fp2 { - re: Fp { + re: BN254 { val: U256([ 0x4ce6cc0166fa7daa, 0xe3d1e7690c43d37b, @@ -250,7 +254,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = { 0x12c85ea5db8c6deb, ]), }, - im: Fp { + im: BN254 { val: U256([ 0x55acdadcd122975b, 0xbc4b313370b38ef3, diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs new file mode 100644 index 00000000..35bbefdd --- /dev/null +++ b/evm/src/extension_tower.rs @@ -0,0 +1,1211 @@ +use std::mem::transmute; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use ethereum_types::{U256, U512}; +use rand::distributions::{Distribution, Standard}; +use rand::Rng; + +pub trait FieldExt: + Copy + + 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 BN_BASE: U256 = U256([ + 0x3c208c16d87cfd47, + 0x97816a916871ca8d, + 0xb85045b68181585d, + 0x30644e72e131a029, +]); + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct BN254 { + pub val: U256, +} + +impl BN254 { + pub fn new(val: usize) -> BN254 { + BN254 { + val: U256::from(val), + } + } +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> BN254 { + let xs = rng.gen::<[u64; 4]>(); + BN254 { + val: U256(xs) % BN_BASE, + } + } +} + +impl Add for BN254 { + type Output = Self; + + fn add(self, other: Self) -> Self { + BN254 { + val: (self.val + other.val) % BN_BASE, + } + } +} + +impl Neg for BN254 { + type Output = Self; + + fn neg(self) -> Self::Output { + BN254 { + val: (BN_BASE - self.val) % BN_BASE, + } + } +} + +impl Sub for BN254 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + BN254 { + val: (BN_BASE + self.val - other.val) % BN_BASE, + } + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Mul for BN254 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + BN254 { + val: U256::try_from((self.val).full_mul(other.val) % BN_BASE).unwrap(), + } + } +} + +impl FieldExt for BN254 { + const ZERO: Self = BN254 { val: U256::zero() }; + const UNIT: Self = BN254 { val: U256::one() }; + fn inv(self) -> BN254 { + let exp = BN_BASE - 2; + let mut current = self; + let mut product = BN254 { val: U256::one() }; + for j in 0..256 { + if exp.bit(j) { + product = product * current; + } + current = current * current; + } + product + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for BN254 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} + +pub const BLS_BASE: U512 = U512([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + 0x0, + 0x0, +]); + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct BLS381 { + pub val: U512, +} + +impl BLS381 { + pub fn new(val: usize) -> BLS381 { + BLS381 { + val: U512::from(val), + } + } +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> BLS381 { + let xs = rng.gen::<[u64; 8]>(); + BLS381 { + val: U512(xs) % BLS_BASE, + } + } +} + +impl Add for BLS381 { + type Output = Self; + + fn add(self, other: Self) -> Self { + BLS381 { + val: (self.val + other.val) % BLS_BASE, + } + } +} + +impl Neg for BLS381 { + type Output = Self; + + fn neg(self) -> Self::Output { + BLS381 { + val: (BLS_BASE - self.val) % BLS_BASE, + } + } +} + +impl Sub for BLS381 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + BLS381 { + val: (BLS_BASE + self.val - other.val) % BLS_BASE, + } + } +} + +impl BLS381 { + fn lsh_128(self) -> BLS381 { + let b128: U512 = U512([0, 0, 1, 0, 0, 0, 0, 0]); + // since BLS_BASE < 2^384, multiplying by 2^128 doesn't overflow the U512 + BLS381 { + val: self.val.saturating_mul(b128) % BLS_BASE, + } + } + + fn lsh_256(self) -> BLS381 { + self.lsh_128().lsh_128() + } + + fn lsh_512(self) -> BLS381 { + self.lsh_256().lsh_256() + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Mul for BLS381 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + // x1, y1 are at most ((q-1) // 2^256) < 2^125 + let x0 = U512(self.val.0[..4].try_into().unwrap()); + let x1 = U512(self.val.0[4..].try_into().unwrap()); + let y0 = U512(other.val.0[..4].try_into().unwrap()); + let y1 = U512(other.val.0[4..].try_into().unwrap()); + + let z00 = BLS381 { + val: x0.saturating_mul(y0) % BLS_BASE, + }; + let z01 = BLS381 { + val: x0.saturating_mul(y1), + }; + let z10 = BLS381 { + val: x1.saturating_mul(y0), + }; + let z11 = BLS381 { + val: x1.saturating_mul(y1), + }; + + z00 + (z01 + z10).lsh_256() + z11.lsh_512() + } +} + +impl FieldExt for BLS381 { + const ZERO: Self = BLS381 { val: U512::zero() }; + const UNIT: Self = BLS381 { val: U512::one() }; + fn inv(self) -> BLS381 { + let exp = BLS_BASE - 2; + let mut current = self; + let mut product = BLS381 { val: U512::one() }; + + for j in 0..512 { + if exp.bit(j) { + product = product * current; + } + current = current * current; + } + product + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for BLS381 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} + +/// The degree 2 field extension Fp2 is given by adjoining i, the square root of -1, to BN254 +/// The arithmetic in this extension is standard complex arithmetic +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Fp2 +where + T: FieldExt, +{ + pub re: T, + pub im: T, +} + +impl Distribution> for Standard +where + T: FieldExt, + Standard: Distribution, +{ + fn sample(&self, rng: &mut R) -> Fp2 { + let (re, im) = rng.gen::<(T, T)>(); + Fp2 { re, im } + } +} + +impl Add for Fp2 { + type Output = Self; + + fn add(self, other: Self) -> Self { + Fp2 { + re: self.re + other.re, + im: self.im + other.im, + } + } +} + +impl Neg for Fp2 { + type Output = Self; + + fn neg(self) -> Self::Output { + Fp2 { + re: -self.re, + im: -self.im, + } + } +} + +impl Sub for Fp2 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + Fp2 { + re: self.re - other.re, + im: self.im - other.im, + } + } +} + +impl Mul for Fp2 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + Fp2 { + re: self.re * other.re - self.im * other.im, + im: self.re * other.im + self.im * other.re, + } + } +} + +/// This function scalar multiplies an Fp2 by an Fp +impl Mul for Fp2 { + type Output = Fp2; + + fn mul(self, other: T) -> Self { + Fp2 { + re: other * self.re, + im: other * self.im, + } + } +} + +impl Fp2 { + /// Return the complex conjugate z' of z: Fp2 + /// This also happens to be the frobenius map + /// z -> z^p + /// since p == 3 mod 4 and hence + /// i^p = i^(4k) * i^3 = 1*(-i) = -i + fn conj(self) -> Self { + Fp2 { + re: self.re, + im: -self.im, + } + } + + // Return the magnitude squared of a complex number + 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' + fn inv(self) -> Fp2 { + let norm_sq = self.norm_sq(); + self.conj() * norm_sq.inv() + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Fp2 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} + +/// This trait defines the method which multiplies +/// by the Fp2 element t^3 whose cube root we will +/// adjoin in the subsequent cubic extension. +/// For BN254 this is 9+i, and for BLS381 it is 1+i. +/// It also defines the relevant FROB constants, +/// given by t^(p^n) and t^(p^2n) for various n, +/// used to compute the frobenius operations. +pub trait Adj: Sized { + fn mul_adj(self) -> Self; + const FROB_T: [[Self; 6]; 2]; + const FROB_Z: [Self; 12]; +} + +impl Adj for Fp2 { + fn mul_adj(self) -> Self { + let nine = BN254::new(9); + Fp2 { + re: nine * self.re - self.im, + im: self.re + nine * self.im, + } + } + + const FROB_T: [[Fp2; 6]; 2] = [ + [ + Fp2 { + re: BN254 { val: U256::one() }, + im: BN254 { val: U256::zero() }, + }, + Fp2 { + re: BN254 { + val: U256([ + 0x99e39557176f553d, + 0xb78cc310c2c3330c, + 0x4c0bec3cf559b143, + 0x2fb347984f7911f7, + ]), + }, + im: BN254 { + val: U256([ + 0x1665d51c640fcba2, + 0x32ae2a1d0b7c9dce, + 0x4ba4cc8bd75a0794, + 0x16c9e55061ebae20, + ]), + }, + }, + Fp2 { + re: BN254 { + val: U256([ + 0xe4bd44e5607cfd48, + 0xc28f069fbb966e3d, + 0x5e6dd9e7e0acccb0, + 0x30644e72e131a029, + ]), + }, + im: BN254 { val: U256::zero() }, + }, + Fp2 { + re: BN254 { + val: U256([ + 0x7b746ee87bdcfb6d, + 0x805ffd3d5d6942d3, + 0xbaff1c77959f25ac, + 0x0856e078b755ef0a, + ]), + }, + im: BN254 { + val: U256([ + 0x380cab2baaa586de, + 0x0fdf31bf98ff2631, + 0xa9f30e6dec26094f, + 0x04f1de41b3d1766f, + ]), + }, + }, + Fp2 { + re: BN254 { + val: U256([ + 0x5763473177fffffe, + 0xd4f263f1acdb5c4f, + 0x59e26bcea0d48bac, + 0x0, + ]), + }, + im: BN254 { val: U256::zero() }, + }, + Fp2 { + re: BN254 { + val: U256([ + 0x62e913ee1dada9e4, + 0xf71614d4b0b71f3a, + 0x699582b87809d9ca, + 0x28be74d4bb943f51, + ]), + }, + im: BN254 { + val: U256([ + 0xedae0bcec9c7aac7, + 0x54f40eb4c3f6068d, + 0xc2b86abcbe01477a, + 0x14a88ae0cb747b99, + ]), + }, + }, + ], + [ + Fp2 { + re: BN254 { val: U256::one() }, + im: BN254 { val: U256::zero() }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x848a1f55921ea762, + 0xd33365f7be94ec72, + 0x80f3c0b75a181e84, + 0x05b54f5e64eea801, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0xc13b4711cd2b8126, + 0x3685d2ea1bdec763, + 0x9f3a80b03b0b1c92, + 0x2c145edbe7fd8aee, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x5763473177fffffe, + 0xd4f263f1acdb5c4f, + 0x59e26bcea0d48bac, + 0x0, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x0e1a92bc3ccbf066, + 0xe633094575b06bcb, + 0x19bee0f7b5b2444e, + 0xbc58c6611c08dab, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0x5fe3ed9d730c239f, + 0xa44a9e08737f96e5, + 0xfeb0f6ef0cd21d04, + 0x23d5e999e1910a12, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xe4bd44e5607cfd48, + 0xc28f069fbb966e3d, + 0x5e6dd9e7e0acccb0, + 0x30644e72e131a029, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xa97bda050992657f, + 0xde1afb54342c724f, + 0x1d9da40771b6f589, + 0x1ee972ae6a826a7d, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0x5721e37e70c255c9, + 0x54326430418536d1, + 0xd2b513cdbb257724, + 0x10de546ff8d4ab51, + ]), + } + }, + }, + ], + ]; + + const FROB_Z: [Fp2; 12] = [ + Fp2 { + re: { BN254 { val: U256::one() } }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xd60b35dadcc9e470, + 0x5c521e08292f2176, + 0xe8b99fdd76e68b60, + 0x1284b71c2865a7df, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0xca5cf05f80f362ac, + 0x747992778eeec7e5, + 0xa6327cfe12150b8e, + 0x246996f3b4fae7e6, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xe4bd44e5607cfd49, + 0xc28f069fbb966e3d, + 0x5e6dd9e7e0acccb0, + 0x30644e72e131a029, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xe86f7d391ed4a67f, + 0x894cb38dbe55d24a, + 0xefe9608cd0acaa90, + 0x19dc81cfcc82e4bb, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0x7694aa2bf4c0c101, + 0x7f03a5e397d439ec, + 0x06cbeee33576139d, + 0xabf8b60be77d73, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0xe4bd44e5607cfd48, + 0xc28f069fbb966e3d, + 0x5e6dd9e7e0acccb0, + 0x30644e72e131a029, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x1264475e420ac20f, + 0x2cfa95859526b0d4, + 0x072fc0af59c61f30, + 0x757cab3a41d3cdc, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0xe85845e34c4a5b9c, + 0xa20b7dfd71573c93, + 0x18e9b79ba4e2606c, + 0xca6b035381e35b6, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x3c208c16d87cfd46, + 0x97816a916871ca8d, + 0xb85045b68181585d, + 0x30644e72e131a029, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x6615563bfbb318d7, + 0x3b2f4c893f42a916, + 0xcf96a5d90a9accfd, + 0x1ddf9756b8cbf849, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0x71c39bb757899a9b, + 0x2307d819d98302a7, + 0x121dc8b86f6c4ccf, + 0x0bfab77f2c36b843, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x5763473177fffffe, + 0xd4f263f1acdb5c4f, + 0x59e26bcea0d48bac, + 0x0, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x53b10eddb9a856c8, + 0x0e34b703aa1bf842, + 0xc866e529b0d4adcd, + 0x1687cca314aebb6d, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0xc58be1eae3bc3c46, + 0x187dc4add09d90a0, + 0xb18456d34c0b44c0, + 0x2fb855bcd54a22b6, + ]), + } + }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x5763473177ffffff, + 0xd4f263f1acdb5c4f, + 0x59e26bcea0d48bac, + 0x0, + ]), + } + }, + im: { BN254 { val: U256::zero() } }, + }, + Fp2 { + re: { + BN254 { + val: U256([ + 0x29bc44b896723b38, + 0x6a86d50bd34b19b9, + 0xb120850727bb392d, + 0x290c83bf3d14634d, + ]), + } + }, + im: { + BN254 { + val: U256([ + 0x53c846338c32a1ab, + 0xf575ec93f71a8df9, + 0x9f668e1adc9ef7f0, + 0x23bd9e3da9136a73, + ]), + } + }, + }, + ]; +} + +impl Adj for Fp2 { + fn mul_adj(self) -> Self { + Fp2 { + re: self.re - self.im, + im: self.re + self.im, + } + } + const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; + const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; +} + +/// 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, + Fp2: Adj, +{ + pub t0: Fp2, + pub t1: Fp2, + pub t2: Fp2, +} + +impl Distribution> for Standard +where + T: FieldExt, + Fp2: Adj, + Standard: Distribution, +{ + fn sample(&self, rng: &mut R) -> Fp6 { + let (t0, t1, t2) = rng.gen::<(Fp2, Fp2, Fp2)>(); + Fp6 { t0, t1, t2 } + } +} + +impl Add for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn add(self, other: Self) -> Self { + Fp6 { + t0: self.t0 + other.t0, + t1: self.t1 + other.t1, + t2: self.t2 + other.t2, + } + } +} + +impl Neg for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn neg(self) -> Self::Output { + Fp6 { + t0: -self.t0, + t1: -self.t1, + t2: -self.t2, + } + } +} + +impl Sub for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn sub(self, other: Self) -> Self { + Fp6 { + t0: self.t0 - other.t0, + t1: self.t1 - other.t1, + t2: self.t2 - other.t2, + } + } +} + +impl Mul for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn mul(self, other: Self) -> Self { + Fp6 { + 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, + } + } +} + +/// This function scalar multiplies an Fp6 by an Fp2 +impl Mul> for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Fp6; + + fn mul(self, other: Fp2) -> Self { + Fp6 { + t0: other * self.t0, + t1: other * self.t1, + t2: other * self.t2, + } + } +} + +impl Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + /// 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 { + Fp6 { + t0: self.t2.mul_adj(), + t1: self.t0, + t2: self.t1, + } + } +} + +impl Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + /// 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 + /// a^(p^n) + b^(p^n) * t^(p^n) + c^(p^n) * t^(2p^n) + /// The Fp2 coefficients are determined by the comment in the conj method, + /// 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 { + let n = n % 6; + let frob_t1 = Fp2::::FROB_T[0][n]; + let frob_t2 = Fp2::::FROB_T[1][n]; + + if n % 2 != 0 { + Fp6 { + t0: self.t0.conj(), + t1: frob_t1 * self.t1.conj(), + t2: frob_t2 * self.t2.conj(), + } + } else { + Fp6 { + t0: self.t0, + t1: frob_t1 * self.t1, + t2: frob_t2 * self.t2, + } + } + } +} + +impl FieldExt for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + 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 + /// (x_n)_m = (x^(p^n))^(p^m) = x^(p^n * p^m) = x^(p^(n+m)) = x_{n+m} + /// By Galois Theory, given x: Fp6, the product + /// phi = x_0 * x_1 * x_2 * x_3 * x_4 * x_5 + /// lands in BN254, and hence the inverse of x is given by + /// (x_1 * x_2 * x_3 * x_4 * x_5) / phi + /// We can save compute by rearranging the numerator: + /// (x_1 * x_3) * x_5 * (x_1 * x_3)_1 + /// By Galois theory, the following are in Fp2 and are complex conjugates + /// x_1 * x_3 * x_5, x_0 * x_2 * x_4 + /// and therefore + /// 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 + 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(); + let prod_odds_over_phi = prod_135 * phi.inv(); + let prod_24 = prod_13.frob(1); + prod_24 * prod_odds_over_phi + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} + +/// 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 +where + T: FieldExt, + Fp2: Adj, +{ + pub z0: Fp6, + pub z1: Fp6, +} + +impl FieldExt for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + const ZERO: Fp12 = Fp12 { + z0: Fp6::::ZERO, + z1: Fp6::::ZERO, + }; + + const UNIT: Fp12 = Fp12 { + z0: Fp6::::UNIT, + z1: Fp6::::ZERO, + }; + + /// By Galois Theory, given x: Fp12, the product + /// phi = Prod_{i=0}^11 x_i + /// lands in BN254, 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 + 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 * phi.inv(); + let prod_evens_except_six = prod_1379.frob(1); + let prod_except_six = prod_evens_except_six * prod_odds_over_phi; + self.conj() * prod_except_six + } +} + +impl Distribution> for Standard +where + T: FieldExt, + Fp2: Adj, + Standard: Distribution, +{ + fn sample(&self, rng: &mut R) -> Fp12 { + let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); + Fp12 { z0, z1 } + } +} + +impl Add for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn add(self, other: Self) -> Self { + Fp12 { + z0: self.z0 + other.z0, + z1: self.z1 + other.z1, + } + } +} + +impl Neg for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn neg(self) -> Self::Output { + Fp12 { + z0: -self.z0, + z1: -self.z1, + } + } +} + +impl Sub for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn sub(self, other: Self) -> Self { + Fp12 { + z0: self.z0 - other.z0, + z1: self.z1 - other.z1, + } + } +} + +impl Mul for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + 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), + } + } +} + +/// This function scalar multiplies an Fp12 by an Fp6 +impl Mul> for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Fp12; + + fn mul(self, other: Fp6) -> Self { + Fp12 { + z0: other * self.z0, + z1: other * self.z1, + } + } +} + +impl Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + fn conj(self) -> Fp12 { + Fp12 { + z0: self.z0, + z1: -self.z1, + } + } +} + +impl Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + /// 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) * (Fp2::::FROB_Z[n]), + } + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} + +pub trait Stack { + fn on_stack(self) -> Vec; +} + +impl Stack for Fp6 { + fn on_stack(self) -> Vec { + let f: [U256; 6] = unsafe { transmute(self) }; + f.into_iter().collect() + } +} + +impl Stack for Fp12 { + fn on_stack(self) -> Vec { + let f: [U256; 12] = unsafe { transmute(self) }; + f.into_iter().collect() + } +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 1313de83..7f62acda 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -5,7 +5,7 @@ use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256}; use plonky2::field::types::Field; -use crate::bn254_arithmetic::Fp12; +use crate::extension_tower::{FieldExt, Fp12, BN254}; use crate::generation::prover_input::EvmField::{ Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; @@ -207,7 +207,7 @@ impl EvmField { } fn field_extension_inverse(&self, n: usize, f: [U256; 12]) -> U256 { - let f: Fp12 = unsafe { transmute(f) }; + let f: Fp12 = unsafe { transmute(f) }; let f_inv: [U256; 12] = unsafe { transmute(f.inv()) }; f_inv[n] } diff --git a/evm/src/lib.rs b/evm/src/lib.rs index b6bb6130..dc2204df 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -8,12 +8,12 @@ pub mod all_stark; pub mod arithmetic; -pub mod bn254_arithmetic; pub mod bn254_pairing; pub mod config; pub mod constraint_consumer; pub mod cpu; pub mod cross_table_lookup; +pub mod extension_tower; pub mod fixed_recursive_verifier; pub mod generation; mod get_challenges;