From 6ac59f16520ac434a7494c545aa0431ee646ea52 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 27 Feb 2023 22:38:37 -0800 Subject: [PATCH 01/39] arithmetic skeleton --- evm/src/bls381_arithmetic.rs | 110 +++++++++++++++++++++++++++++++++++ evm/src/lib.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 evm/src/bls381_arithmetic.rs diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs new file mode 100644 index 00000000..3c860821 --- /dev/null +++ b/evm/src/bls381_arithmetic.rs @@ -0,0 +1,110 @@ +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use ethereum_types::U512; +use rand::distributions::{Distribution, Standard}; +use rand::Rng; + +pub const BLS_BASE: U512 = U512([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + 0x0, + 0x0, +]); + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Fp { + pub val: U512, +} + +impl Fp { + pub fn new(val: usize) -> Fp { + Fp { + val: U512::from(val), + } + } +} + +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; + + fn add(self, other: Self) -> Self { + Fp { + val: (self.val + other.val) % BLS_BASE, + } + } +} + +impl Neg for Fp { + type Output = Self; + + fn neg(self) -> Self::Output { + Fp { + val: (BLS_BASE - self.val) % BLS_BASE, + } + } +} + +impl Sub for Fp { + type Output = Self; + + fn sub(self, other: Self) -> Self { + Fp { + val: (BLS_BASE + self.val - other.val) % BLS_BASE, + } + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Mul for Fp { + type Output = Self; + + fn mul(self, other: Self) -> Self { + Fp { + val: (self.val).overflowing_mul(other.val).0 % BLS_BASE, + } + } +} + +impl Fp { + pub fn inv(self) -> Fp { + exp_fp(self, BLS_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: 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() }; + + for j in 0..512 { + if e.bit(j) { + product = product * current; + } + current = current * current; + } + product +} diff --git a/evm/src/lib.rs b/evm/src/lib.rs index b6bb6130..61c5a07c 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -8,6 +8,7 @@ pub mod all_stark; pub mod arithmetic; +pub mod bls381_arithmetic; pub mod bn254_arithmetic; pub mod bn254_pairing; pub mod config; From 2c73d5d7ddf0528d34981a9f20e949711d2c6e58 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 27 Feb 2023 22:55:31 -0800 Subject: [PATCH 02/39] bls field arithmetic --- evm/src/bls381_arithmetic.rs | 391 ++++++++++++++++++++++++++++++++++- 1 file changed, 390 insertions(+), 1 deletion(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index 3c860821..8db0ce3c 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -1,3 +1,4 @@ +use std::mem::transmute; use std::ops::{Add, Div, Mul, Neg, Sub}; use ethereum_types::U512; @@ -30,7 +31,7 @@ impl Fp { impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Fp { - let xs = rng.gen::<[u64;8]>(); + let xs = rng.gen::<[u64; 8]>(); Fp { val: U512(xs) % BLS_BASE, } @@ -108,3 +109,391 @@ fn exp_fp(x: Fp, e: U512) -> Fp { } 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 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 { + 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 = 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, +} + +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).i1(), + t1: self.t0 * other.t1 + self.t1 * other.t0 + (self.t2 * other.t2).i1(), + 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 1+i as the 1 coefficient of the output + fn sh(self) -> Fp6 { + Fp6 { + t0: self.t2.i1(), + 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: [U512; 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: [U512; 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] = [ZERO_FP2; 6]; + +const FROB_T2: [Fp2; 6] = [ZERO_FP2; 6]; + +const FROB_Z: [Fp2; 12] = [ZERO_FP2; 12]; From bde5c557a6264352c1b8bd32b5b2f73e20c70028 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 28 Feb 2023 17:16:43 -0800 Subject: [PATCH 03/39] correct mul impl --- evm/src/bls381_arithmetic.rs | 40 +++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index 8db0ce3c..88e27596 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -68,14 +68,48 @@ impl Sub for Fp { } } +impl Fp { + fn lsh_128(self) -> Fp { + 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 + Fp { + val: self.val.saturating_mul(b128) % BLS_BASE, + } + } + + fn lsh_256(self) -> Fp { + self.lsh_128().lsh_128() + } + + fn lsh_512(self) -> Fp { + self.lsh_256().lsh_256() + } +} + #[allow(clippy::suspicious_arithmetic_impl)] impl Mul for Fp { type Output = Self; fn mul(self, other: Self) -> Self { - Fp { - val: (self.val).overflowing_mul(other.val).0 % BLS_BASE, - } + let b256: U512 = U512([0, 0, 0, 0, 1, 0, 0, 0]); + // x1, y1 are at most (q-1) // 2^256 < 2^125 + let (x0, x1) = self.val.div_mod(b256); + let (y0, y1) = other.val.div_mod(b256); + + let z00 = Fp { + val: x0.saturating_mul(y0) % BLS_BASE, + }; + let z01 = Fp { + val: x0.saturating_mul(y1), + }; + let z10 = Fp { + val: x1.saturating_mul(y0), + }; + let z11 = Fp { + val: x1.saturating_mul(y1), + }; + + z00 + (z01 + z10).lsh_256() + z11.lsh_512() } } From ab32f03b107fdfef64a231fae53c7df0aa253cd4 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 28 Feb 2023 18:34:22 -0800 Subject: [PATCH 04/39] fixed multiplication --- evm/src/bls381_arithmetic.rs | 4 ++-- evm/src/cpu/kernel/tests/bls381.rs | 16 ++++++++++++++++ evm/src/cpu/kernel/tests/mod.rs | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 evm/src/cpu/kernel/tests/bls381.rs diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index 88e27596..f25bce79 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -93,8 +93,8 @@ impl Mul for Fp { fn mul(self, other: Self) -> Self { let b256: U512 = U512([0, 0, 0, 0, 1, 0, 0, 0]); // x1, y1 are at most (q-1) // 2^256 < 2^125 - let (x0, x1) = self.val.div_mod(b256); - let (y0, y1) = other.val.div_mod(b256); + let (x1, x0) = self.val.div_mod(b256); + let (y1, y0) = other.val.div_mod(b256); let z00 = Fp { val: x0.saturating_mul(y0) % BLS_BASE, diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs new file mode 100644 index 00000000..8b56abf1 --- /dev/null +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -0,0 +1,16 @@ +use crate::bls381_arithmetic::Fp; +use rand::Rng; + +#[test] +fn test_bls_mul() -> Result<(),()> { + let mut rng = rand::thread_rng(); + let f: Fp = rng.gen::(); + let g: Fp = rng.gen::(); + let fg = f*g; + + println!("{:#?}", f); + println!("{:#?}", g); + println!("{:#?}", fg); + + Ok(()) +} \ No newline at end of file diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index aab8298d..a0452d34 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,5 +1,6 @@ mod account_code; mod balance; +mod bls381; mod bn254; mod core; mod ecc; From 1ee39b51c1ee695e2ca9aa6a041e4c0993b67412 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 28 Feb 2023 18:57:17 -0800 Subject: [PATCH 05/39] fmt --- evm/src/cpu/kernel/tests/bls381.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs index 8b56abf1..81b9fa63 100644 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -1,16 +1,17 @@ -use crate::bls381_arithmetic::Fp; use rand::Rng; +use crate::bls381_arithmetic::Fp; + #[test] -fn test_bls_mul() -> Result<(),()> { +fn test_bls_mul() -> Result<(), ()> { let mut rng = rand::thread_rng(); let f: Fp = rng.gen::(); let g: Fp = rng.gen::(); - let fg = f*g; + let fg = f * g; println!("{:#?}", f); println!("{:#?}", g); println!("{:#?}", fg); Ok(()) -} \ No newline at end of file +} From a96418b36c2352df3cf435c813692100c176984f Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 8 Mar 2023 15:06:45 -0800 Subject: [PATCH 06/39] unused test --- evm/src/bls381_pairing.rs | 0 evm/src/cpu/kernel/tests/bls381.rs | 17 ----------------- evm/src/cpu/kernel/tests/mod.rs | 1 - 3 files changed, 18 deletions(-) create mode 100644 evm/src/bls381_pairing.rs delete mode 100644 evm/src/cpu/kernel/tests/bls381.rs diff --git a/evm/src/bls381_pairing.rs b/evm/src/bls381_pairing.rs new file mode 100644 index 00000000..e69de29b diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs deleted file mode 100644 index 81b9fa63..00000000 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ /dev/null @@ -1,17 +0,0 @@ -use rand::Rng; - -use crate::bls381_arithmetic::Fp; - -#[test] -fn test_bls_mul() -> Result<(), ()> { - let mut rng = rand::thread_rng(); - let f: Fp = rng.gen::(); - let g: Fp = rng.gen::(); - let fg = f * g; - - println!("{:#?}", f); - println!("{:#?}", g); - println!("{:#?}", fg); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index a0452d34..aab8298d 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,6 +1,5 @@ mod account_code; mod balance; -mod bls381; mod bn254; mod core; mod ecc; From 625bdb680bdec50458926652acd5451f3d35676e Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Sat, 11 Mar 2023 12:39:18 -0800 Subject: [PATCH 07/39] skeleton --- evm/src/bls381_arithmetic.rs | 415 +++++++++++++++++++---------------- 1 file changed, 224 insertions(+), 191 deletions(-) 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; +// } From 05788a997df9b1f338d72405b5168c87779ef072 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 14 Mar 2023 15:16:24 -0700 Subject: [PATCH 08/39] compiles --- evm/src/bls381_arithmetic.rs | 263 ++++++++++++++++++++--------------- 1 file changed, 151 insertions(+), 112 deletions(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index d2e2cac3..bf886e1c 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -1,11 +1,13 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; -use ethereum_types::{U512}; +use ethereum_types::U512; + // use rand::distributions::{Distribution, Standard}; // use rand::Rng; pub trait FieldExt: Sized + + Copy + std::ops::Add + std::ops::Neg + std::ops::Sub @@ -158,12 +160,14 @@ 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 where T: FieldExt { +pub struct Fp2 +where + T: FieldExt, +{ pub re: T, pub im: T, } - // impl> Distribution> for Standard { // fn sample(&self, rng: &mut R) -> Fp2 { // let (re, im) = rng.gen::<(T, T)>(); @@ -182,7 +186,7 @@ impl Add for Fp2 { } } -impl Neg for Fp2 { +impl Neg for Fp2 { type Output = Self; fn neg(self) -> Self::Output { @@ -193,7 +197,7 @@ impl Neg for Fp2 { } } -impl Sub for Fp2 { +impl Sub for Fp2 { type Output = Self; fn sub(self, other: Self) -> Self { @@ -204,9 +208,7 @@ impl Sub for Fp2 { } } -impl Mul - for Fp2 -{ +impl Mul for Fp2 { type Output = Self; fn mul(self, other: Self) -> Self { @@ -270,13 +272,13 @@ impl Div for Fp2 { } } -trait Adj { +pub trait Adj { fn mul_adj(self) -> Self; } /// Helper function which multiplies by the Fp2 element /// whose cube root we will adjoin in the next extension -impl Adj for Fp2 { +impl Adj for Fp2 { fn mul_adj(self) -> Self { Fp2 { re: self.re - self.im, @@ -288,14 +290,15 @@ impl Adj 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 where T: FieldExt { +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)>(); @@ -374,12 +377,12 @@ impl Fp6 { } } -pub trait Frob { - const FROB_T: Self; - const FROB_Z: Self; +impl Fp6 { + const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; + const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; } -impl Fp6 { +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 @@ -390,8 +393,8 @@ impl Fp6 { /// 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 = Self::FROB_T[0][n]; - let frob_t2 = Self::FROB_T[1][n]; + let frob_t1 = Fp6::::FROB_T[0][n]; + let frob_t2 = Fp6::::FROB_T[1][n]; if n % 2 != 0 { Fp6 { @@ -409,7 +412,7 @@ impl Fp6 { } } -impl FieldExt for Fp6 { +impl FieldExt for Fp6 { const ZERO: Fp6 = Fp6 { t0: Fp2::::ZERO, t1: Fp2::::ZERO, @@ -456,110 +459,146 @@ 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 +where + T: FieldExt, +{ + pub z0: Fp6, + pub z1: Fp6, +} -// impl Unital for Fp12 { -// const ZERO: Fp12 = Fp12 { -// z0: Fp6::::ZERO, -// z1: Fp6::::ZERO, -// }; +impl FieldExt for Fp12 { + const ZERO: Fp12 = Fp12 { + z0: Fp6::::ZERO, + z1: Fp6::::ZERO, + }; -// const UNIT: Fp12 = Fp12 { -// z0: Fp6::::UNIT, -// z1: Fp6::::ZERO, -// }; -// } + const UNIT: Fp12 = Fp12 { + z0: Fp6::::UNIT, + z1: Fp6::::ZERO, + }; -// // impl> Distribution> for Standard { -// // fn sample(&self, rng: &mut R) -> Fp12 { -// // let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); -// // Fp12 { z0, 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 + 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) + } +} -// 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> Distribution> for Standard { +// fn sample(&self, rng: &mut R) -> Fp12 { +// let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); +// Fp12 { z0, z1 } // } // } -// 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, -// } -// } +impl Add for Fp12 { + type Output = Self; -// fn conj(self) -> Fp12 { -// Fp12 { -// z0: self.z0, -// z1: -self.z1, -// } -// } -// } + fn add(self, other: Self) -> Self { + Fp12 { + z0: self.z0 + other.z0, + z1: self.z1 + other.z1, + } + } +} -// 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]), -// } -// } +impl Neg 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 neg(self) -> Self::Output { + Fp12 { + z0: -self.z0, + z1: -self.z1, + } + } +} -// #[allow(clippy::suspicious_arithmetic_impl)] -// impl> Div for Fp12 { -// type Output = Self; +impl Sub for Fp12 { + type Output = Self; -// fn div(self, rhs: Self) -> Self::Output { -// self * rhs.inv() -// } -// } + fn sub(self, other: Self) -> Self { + Fp12 { + z0: self.z0 - other.z0, + z1: self.z1 - other.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, + } + } +} + +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(Fp6::::FROB_Z[n]), + } + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for Fp12 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inv() + } +} // trait Stack { // fn on_stack(self) -> Vec; From 9ec9774410c8fec0267d6c0ab72676c85f3d8137 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 14 Mar 2023 17:29:38 -0700 Subject: [PATCH 09/39] run_ops --- evm/src/bls381_arithmetic.rs | 60 +++++++++++----------- evm/src/cpu/kernel/interpreter.rs | 85 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 30 deletions(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index bf886e1c..4a9daedd 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -31,77 +31,77 @@ pub const BLS_BASE: U512 = U512([ ]); #[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp { +pub struct Fp381 { pub val: U512, } -impl Fp { - pub fn new(val: usize) -> Fp { - Fp { +impl Fp381 { + pub fn new(val: usize) -> Fp381 { + Fp381 { val: U512::from(val), } } } -// impl Distribution for Standard { -// fn sample(&self, rng: &mut R) -> Fp { +// impl Distribution for Standard { +// fn sample(&self, rng: &mut R) -> Fp381 { // let xs = rng.gen::<[u64; 8]>(); -// Fp { +// Fp381 { // val: U512(xs) % BLS_BASE, // } // } // } -impl Add for Fp { +impl Add for Fp381 { type Output = Self; fn add(self, other: Self) -> Self { - Fp { + Fp381 { val: (self.val + other.val) % BLS_BASE, } } } -impl Neg for Fp { +impl Neg for Fp381 { type Output = Self; fn neg(self) -> Self::Output { - Fp { + Fp381 { val: (BLS_BASE - self.val) % BLS_BASE, } } } -impl Sub for Fp { +impl Sub for Fp381 { type Output = Self; fn sub(self, other: Self) -> Self { - Fp { + Fp381 { val: (BLS_BASE + self.val - other.val) % BLS_BASE, } } } -impl Fp { - fn lsh_128(self) -> Fp { +impl Fp381 { + fn lsh_128(self) -> Fp381 { 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 - Fp { + Fp381 { val: self.val.saturating_mul(b128) % BLS_BASE, } } - fn lsh_256(self) -> Fp { + fn lsh_256(self) -> Fp381 { self.lsh_128().lsh_128() } - fn lsh_512(self) -> Fp { + fn lsh_512(self) -> Fp381 { self.lsh_256().lsh_256() } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for Fp { +impl Mul for Fp381 { type Output = Self; fn mul(self, other: Self) -> Self { @@ -110,16 +110,16 @@ impl Mul for Fp { let (x1, x0) = self.val.div_mod(b256); let (y1, y0) = other.val.div_mod(b256); - let z00 = Fp { + let z00 = Fp381 { val: x0.saturating_mul(y0) % BLS_BASE, }; - let z01 = Fp { + let z01 = Fp381 { val: x0.saturating_mul(y1), }; - let z10 = Fp { + let z10 = Fp381 { val: x1.saturating_mul(y0), }; - let z11 = Fp { + let z11 = Fp381 { val: x1.saturating_mul(y1), }; @@ -127,16 +127,16 @@ impl Mul for Fp { } } -impl FieldExt for Fp { - const ZERO: Self = Fp { val: U512::zero() }; - const UNIT: Self = Fp { val: U512::one() }; - fn inv(self) -> Fp { +impl FieldExt for Fp381 { + const ZERO: Self = Fp381 { val: U512::zero() }; + const UNIT: Self = Fp381 { val: U512::one() }; + fn inv(self) -> Fp381 { exp_fp(self, BLS_BASE - 2) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp { +impl Div for Fp381 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -144,9 +144,9 @@ impl Div for Fp { } } -fn exp_fp(x: Fp, e: U512) -> Fp { +fn exp_fp(x: Fp381, e: U512) -> Fp381 { let mut current = x; - let mut product = Fp { val: U512::one() }; + let mut product = Fp381 { val: U512::one() }; for j in 0..512 { if e.bit(j) { diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 52876c97..42e67424 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -8,6 +8,7 @@ use ethereum_types::{U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; +use crate::bls381_arithmetic::{Fp381, BLS_BASE}; use crate::bn254_arithmetic::BN_BASE; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; @@ -438,6 +439,90 @@ impl<'a> Interpreter<'a> { self.push((x + (BN_BASE - y)) % BN_BASE); } + #[allow(dead_code)] + fn run_addfp381_lo(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (x + y) % BLS_BASE; + + self.push(U256(z.0[0..4].try_into().unwrap())); + } + + #[allow(dead_code)] + fn run_addfp381_hi(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (x + y) % BLS_BASE; + + self.push(U256(z.0[4..].try_into().unwrap())); + } + + #[allow(dead_code)] + fn run_mulfp254_lo(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (Fp381 {val: x} * Fp381 {val: y}).val; + + self.push(U256(z.0[0..4].try_into().unwrap())); + } + + #[allow(dead_code)] + fn run_mulfp254_hi(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (Fp381 {val: x} * Fp381 {val: y}).val; + + self.push(U256(z.0[4..].try_into().unwrap())); + } + + #[allow(dead_code)] + fn run_subfp381_lo(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (BLS_BASE + x - y) % BLS_BASE; + + self.push(U256(z.0[0..4].try_into().unwrap())); + } + + #[allow(dead_code)] + fn run_subfp381_hi(&mut self) { + let x1 = self.pop(); + let x0 = self.pop(); + let y1 = self.pop(); + let y0 = self.pop(); + + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = (BLS_BASE + x - y) % BLS_BASE; + + self.push(U256(z.0[4..].try_into().unwrap())); + } + fn run_div(&mut self) { let x = self.pop(); let y = self.pop(); From a8956b9408c43f7f80438065d793f0f03572e260 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 14 Mar 2023 17:31:55 -0700 Subject: [PATCH 10/39] flip limbs --- evm/src/cpu/kernel/interpreter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 42e67424..92a2e2ec 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -450,7 +450,7 @@ impl<'a> Interpreter<'a> { let y = U512::from(y0) + (U512::from(y1) << 256); let z = (x + y) % BLS_BASE; - self.push(U256(z.0[0..4].try_into().unwrap())); + self.push(U256(z.0[4..].try_into().unwrap())); } #[allow(dead_code)] @@ -464,7 +464,7 @@ impl<'a> Interpreter<'a> { let y = U512::from(y0) + (U512::from(y1) << 256); let z = (x + y) % BLS_BASE; - self.push(U256(z.0[4..].try_into().unwrap())); + self.push(U256(z.0[..4].try_into().unwrap())); } #[allow(dead_code)] @@ -476,9 +476,9 @@ impl<'a> Interpreter<'a> { let x = U512::from(x0) + (U512::from(x1) << 256); let y = U512::from(y0) + (U512::from(y1) << 256); - let z = (Fp381 {val: x} * Fp381 {val: y}).val; + let z = (Fp381 { val: x } * Fp381 { val: y }).val; - self.push(U256(z.0[0..4].try_into().unwrap())); + self.push(U256(z.0[4..].try_into().unwrap())); } #[allow(dead_code)] @@ -490,9 +490,9 @@ impl<'a> Interpreter<'a> { let x = U512::from(x0) + (U512::from(x1) << 256); let y = U512::from(y0) + (U512::from(y1) << 256); - let z = (Fp381 {val: x} * Fp381 {val: y}).val; + let z = (Fp381 { val: x } * Fp381 { val: y }).val; - self.push(U256(z.0[4..].try_into().unwrap())); + self.push(U256(z.0[..4].try_into().unwrap())); } #[allow(dead_code)] @@ -506,7 +506,7 @@ impl<'a> Interpreter<'a> { let y = U512::from(y0) + (U512::from(y1) << 256); let z = (BLS_BASE + x - y) % BLS_BASE; - self.push(U256(z.0[0..4].try_into().unwrap())); + self.push(U256(z.0[4..].try_into().unwrap())); } #[allow(dead_code)] @@ -520,7 +520,7 @@ impl<'a> Interpreter<'a> { let y = U512::from(y0) + (U512::from(y1) << 256); let z = (BLS_BASE + x - y) % BLS_BASE; - self.push(U256(z.0[4..].try_into().unwrap())); + self.push(U256(z.0[..4].try_into().unwrap())); } fn run_div(&mut self) { From 459d2929643f8f0d18ca30254e51c713b09f6be2 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 15 Mar 2023 19:34:33 -0700 Subject: [PATCH 11/39] folder --- evm/src/cpu/kernel/asm/curve/bls381/util.asm | 0 evm/src/cpu/kernel/interpreter.rs | 45 ++------------------ 2 files changed, 3 insertions(+), 42 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/curve/bls381/util.asm diff --git a/evm/src/cpu/kernel/asm/curve/bls381/util.asm b/evm/src/cpu/kernel/asm/curve/bls381/util.asm new file mode 100644 index 00000000..e69de29b diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 92a2e2ec..ecd0e3bc 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -440,7 +440,7 @@ impl<'a> Interpreter<'a> { } #[allow(dead_code)] - fn run_addfp381_lo(&mut self) { + fn run_addfp381(&mut self) { let x1 = self.pop(); let x0 = self.pop(); let y1 = self.pop(); @@ -451,24 +451,11 @@ impl<'a> Interpreter<'a> { let z = (x + y) % BLS_BASE; self.push(U256(z.0[4..].try_into().unwrap())); - } - - #[allow(dead_code)] - fn run_addfp381_hi(&mut self) { - let x1 = self.pop(); - let x0 = self.pop(); - let y1 = self.pop(); - let y0 = self.pop(); - - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = (x + y) % BLS_BASE; - self.push(U256(z.0[..4].try_into().unwrap())); } #[allow(dead_code)] - fn run_mulfp254_lo(&mut self) { + fn run_mulfp381(&mut self) { let x1 = self.pop(); let x0 = self.pop(); let y1 = self.pop(); @@ -479,24 +466,11 @@ impl<'a> Interpreter<'a> { let z = (Fp381 { val: x } * Fp381 { val: y }).val; self.push(U256(z.0[4..].try_into().unwrap())); - } - - #[allow(dead_code)] - fn run_mulfp254_hi(&mut self) { - let x1 = self.pop(); - let x0 = self.pop(); - let y1 = self.pop(); - let y0 = self.pop(); - - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = (Fp381 { val: x } * Fp381 { val: y }).val; - self.push(U256(z.0[..4].try_into().unwrap())); } #[allow(dead_code)] - fn run_subfp381_lo(&mut self) { + fn run_subfp381(&mut self) { let x1 = self.pop(); let x0 = self.pop(); let y1 = self.pop(); @@ -507,19 +481,6 @@ impl<'a> Interpreter<'a> { let z = (BLS_BASE + x - y) % BLS_BASE; self.push(U256(z.0[4..].try_into().unwrap())); - } - - #[allow(dead_code)] - fn run_subfp381_hi(&mut self) { - let x1 = self.pop(); - let x0 = self.pop(); - let y1 = self.pop(); - let y0 = self.pop(); - - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = (BLS_BASE + x - y) % BLS_BASE; - self.push(U256(z.0[..4].try_into().unwrap())); } From 6f6c808d80d8836a10f86a94c742089821020e7d Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 15 Mar 2023 19:41:55 -0700 Subject: [PATCH 12/39] more efficient divmod --- evm/src/bls381_arithmetic.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index bf886e1c..f3217639 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -105,10 +105,11 @@ impl Mul for Fp { type Output = Self; fn mul(self, other: Self) -> Self { - let b256: U512 = U512([0, 0, 0, 0, 1, 0, 0, 0]); // x1, y1 are at most (q-1) // 2^256 < 2^125 - let (x1, x0) = self.val.div_mod(b256); - let (y1, y0) = other.val.div_mod(b256); + let x1 = U512(self.val.0[..4].try_into().unwrap()); + let x0 = U512(self.val.0[4..].try_into().unwrap()); + let y1 = U512(other.val.0[..4].try_into().unwrap()); + let y0 = U512(other.val.0[4..].try_into().unwrap()); let z00 = Fp { val: x0.saturating_mul(y0) % BLS_BASE, From 25575df57c95fa61e3be96c0ba885e221c65fda6 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 20 Mar 2023 11:49:13 -0700 Subject: [PATCH 13/39] cleanup --- evm/src/bls381_arithmetic.rs | 76 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs index f3217639..f47467c7 100644 --- a/evm/src/bls381_arithmetic.rs +++ b/evm/src/bls381_arithmetic.rs @@ -1,9 +1,8 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; use ethereum_types::U512; - -// use rand::distributions::{Distribution, Standard}; -// use rand::Rng; +use rand::distributions::{Distribution, Standard}; +use rand::Rng; pub trait FieldExt: Sized @@ -43,14 +42,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; @@ -105,11 +104,11 @@ impl Mul for Fp { type Output = Self; fn mul(self, other: Self) -> Self { - // x1, y1 are at most (q-1) // 2^256 < 2^125 - let x1 = U512(self.val.0[..4].try_into().unwrap()); - let x0 = U512(self.val.0[4..].try_into().unwrap()); - let y1 = U512(other.val.0[..4].try_into().unwrap()); - let y0 = U512(other.val.0[4..].try_into().unwrap()); + // 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 = Fp { val: x0.saturating_mul(y0) % BLS_BASE, @@ -169,12 +168,15 @@ where pub im: T, } -// impl> Distribution> for Standard { -// fn sample(&self, rng: &mut R) -> Fp2 { -// let (re, im) = rng.gen::<(T, T)>(); -// Fp2 { re, im } -// } -// } +impl + FieldExt> Distribution> for Standard +where + 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; @@ -300,12 +302,15 @@ where 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 + FieldExt> Distribution> for Standard +where + 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 { type Output = Self; @@ -506,12 +511,15 @@ impl FieldExt for Fp12 { } } -// impl> Distribution> for Standard { -// fn sample(&self, rng: &mut R) -> Fp12 { -// let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); -// Fp12 { z0, z1 } -// } -// } +impl + FieldExt> Distribution> for Standard +where + Standard: Distribution, +{ + fn sample(&self, rng: &mut R) -> Fp12 { + let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); + Fp12 { z0, z1 } + } +} impl Add for Fp12 { type Output = Self; From 503880731a871ea6f90f751b9e7eb5d2f57ae0b7 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 20 Mar 2023 11:55:43 -0700 Subject: [PATCH 14/39] rename --- evm/src/bls381_pairing.rs | 0 evm/src/{bls381_arithmetic.rs => extension_tower.rs} | 0 evm/src/lib.rs | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 evm/src/bls381_pairing.rs rename evm/src/{bls381_arithmetic.rs => extension_tower.rs} (100%) diff --git a/evm/src/bls381_pairing.rs b/evm/src/bls381_pairing.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/extension_tower.rs similarity index 100% rename from evm/src/bls381_arithmetic.rs rename to evm/src/extension_tower.rs diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 61c5a07c..fa48d8dc 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -8,13 +8,13 @@ pub mod all_stark; pub mod arithmetic; -pub mod bls381_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; From 5e98a5f90722d5594357b818daac5ba0ef3a18ef Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 20 Mar 2023 12:25:23 -0700 Subject: [PATCH 15/39] adj trait --- evm/src/extension_tower.rs | 183 ++++++++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 52 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index f47467c7..3a8d7443 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -30,77 +30,77 @@ pub const BLS_BASE: U512 = U512([ ]); #[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp { +pub struct Fp381 { pub val: U512, } -impl Fp { - pub fn new(val: usize) -> Fp { - Fp { +impl Fp381 { + pub fn new(val: usize) -> Fp381 { + Fp381 { val: U512::from(val), } } } -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp { +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Fp381 { let xs = rng.gen::<[u64; 8]>(); - Fp { + Fp381 { val: U512(xs) % BLS_BASE, } } } -impl Add for Fp { +impl Add for Fp381 { type Output = Self; fn add(self, other: Self) -> Self { - Fp { + Fp381 { val: (self.val + other.val) % BLS_BASE, } } } -impl Neg for Fp { +impl Neg for Fp381 { type Output = Self; fn neg(self) -> Self::Output { - Fp { + Fp381 { val: (BLS_BASE - self.val) % BLS_BASE, } } } -impl Sub for Fp { +impl Sub for Fp381 { type Output = Self; fn sub(self, other: Self) -> Self { - Fp { + Fp381 { val: (BLS_BASE + self.val - other.val) % BLS_BASE, } } } -impl Fp { - fn lsh_128(self) -> Fp { +impl Fp381 { + fn lsh_128(self) -> Fp381 { 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 - Fp { + Fp381 { val: self.val.saturating_mul(b128) % BLS_BASE, } } - fn lsh_256(self) -> Fp { + fn lsh_256(self) -> Fp381 { self.lsh_128().lsh_128() } - fn lsh_512(self) -> Fp { + fn lsh_512(self) -> Fp381 { self.lsh_256().lsh_256() } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for Fp { +impl Mul for Fp381 { type Output = Self; fn mul(self, other: Self) -> Self { @@ -110,16 +110,16 @@ impl Mul for Fp { let y0 = U512(other.val.0[..4].try_into().unwrap()); let y1 = U512(other.val.0[4..].try_into().unwrap()); - let z00 = Fp { + let z00 = Fp381 { val: x0.saturating_mul(y0) % BLS_BASE, }; - let z01 = Fp { + let z01 = Fp381 { val: x0.saturating_mul(y1), }; - let z10 = Fp { + let z10 = Fp381 { val: x1.saturating_mul(y0), }; - let z11 = Fp { + let z11 = Fp381 { val: x1.saturating_mul(y1), }; @@ -127,16 +127,16 @@ impl Mul for Fp { } } -impl FieldExt for Fp { - const ZERO: Self = Fp { val: U512::zero() }; - const UNIT: Self = Fp { val: U512::one() }; - fn inv(self) -> Fp { +impl FieldExt for Fp381 { + const ZERO: Self = Fp381 { val: U512::zero() }; + const UNIT: Self = Fp381 { val: U512::one() }; + fn inv(self) -> Fp381 { exp_fp(self, BLS_BASE - 2) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp { +impl Div for Fp381 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -144,9 +144,9 @@ impl Div for Fp { } } -fn exp_fp(x: Fp, e: U512) -> Fp { +fn exp_fp(x: Fp381, e: U512) -> Fp381 { let mut current = x; - let mut product = Fp { val: U512::one() }; + let mut product = Fp381 { val: U512::one() }; for j in 0..512 { if e.bit(j) { @@ -168,8 +168,9 @@ where pub im: T, } -impl + FieldExt> Distribution> for Standard +impl Distribution> for Standard where + T: Distribution + FieldExt, Standard: Distribution, { fn sample(&self, rng: &mut R) -> Fp2 { @@ -281,7 +282,7 @@ pub trait Adj { /// Helper function which multiplies by the Fp2 element /// whose cube root we will adjoin in the next extension -impl Adj for Fp2 { +impl Adj for Fp2 { fn mul_adj(self) -> Self { Fp2 { re: self.re - self.im, @@ -296,14 +297,17 @@ impl Adj for Fp2 { pub struct Fp6 where T: FieldExt, + Fp2: Adj, { pub t0: Fp2, pub t1: Fp2, pub t2: Fp2, } -impl + FieldExt> Distribution> for Standard +impl Distribution> for Standard where + T: Distribution + FieldExt, + Fp2: Adj, Standard: Distribution, { fn sample(&self, rng: &mut R) -> Fp6 { @@ -312,7 +316,11 @@ where } } -impl Add for Fp6 { +impl Add for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn add(self, other: Self) -> Self { @@ -324,7 +332,11 @@ impl Add for Fp6 { } } -impl Neg for Fp6 { +impl Neg for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn neg(self) -> Self::Output { @@ -336,7 +348,11 @@ impl Neg for Fp6 { } } -impl Sub for Fp6 { +impl Sub for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn sub(self, other: Self) -> Self { @@ -348,7 +364,11 @@ impl Sub for Fp6 { } } -impl Mul for Fp6 { +impl Mul for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn mul(self, other: Self) -> Self { @@ -360,7 +380,11 @@ impl Mul for Fp6 { } } -impl Fp6 { +impl Fp6 +where + T: FieldExt, + Fp2: Adj, +{ // This function scalar multiplies an Fp6 by an Fp2 fn scale(self, x: Fp2) -> Fp6 { Fp6 { @@ -371,7 +395,11 @@ impl Fp6 { } } -impl Fp6 { +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 { @@ -383,12 +411,20 @@ impl Fp6 { } } -impl Fp6 { +impl Fp6 +where + T: FieldExt, + Fp2: Adj, +{ const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; } -impl Fp6 { +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 @@ -418,7 +454,11 @@ impl Fp6 { } } -impl FieldExt for Fp6 { +impl FieldExt for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ const ZERO: Fp6 = Fp6 { t0: Fp2::::ZERO, t1: Fp2::::ZERO, @@ -457,7 +497,11 @@ impl FieldExt for Fp6 { } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp6 { +impl Div for Fp6 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -471,12 +515,17 @@ impl Div for Fp6 { pub struct Fp12 where T: FieldExt, + Fp2: Adj, { pub z0: Fp6, pub z1: Fp6, } -impl FieldExt for Fp12 { +impl FieldExt for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ const ZERO: Fp12 = Fp12 { z0: Fp6::::ZERO, z1: Fp6::::ZERO, @@ -511,8 +560,10 @@ impl FieldExt for Fp12 { } } -impl + FieldExt> Distribution> for Standard +impl Distribution> for Standard where + T: Distribution + FieldExt, + Fp2: Adj, Standard: Distribution, { fn sample(&self, rng: &mut R) -> Fp12 { @@ -521,7 +572,11 @@ where } } -impl Add for Fp12 { +impl Add for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn add(self, other: Self) -> Self { @@ -532,7 +587,11 @@ impl Add for Fp12 { } } -impl Neg for Fp12 { +impl Neg for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn neg(self) -> Self::Output { @@ -543,7 +602,11 @@ impl Neg for Fp12 { } } -impl Sub for Fp12 { +impl Sub for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn sub(self, other: Self) -> Self { @@ -554,7 +617,11 @@ impl Sub for Fp12 { } } -impl Mul for Fp12 { +impl Mul for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn mul(self, other: Self) -> Self { @@ -568,7 +635,11 @@ impl Mul for Fp12 { } } -impl Fp12 { +impl Fp12 +where + T: FieldExt, + Fp2: Adj, +{ // This function scalar multiplies an Fp12 by an Fp6 fn scale(self, x: Fp6) -> Fp12 { Fp12 { @@ -585,7 +656,11 @@ impl Fp12 { } } -impl Fp12 { +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 @@ -601,7 +676,11 @@ impl Fp12 { } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp12 { +impl Div for Fp12 +where + T: FieldExt, + Fp2: Adj, +{ type Output = Self; fn div(self, rhs: Self) -> Self::Output { From caaf3b4a34538db00abbfe80a786c742779ce76c Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 11:42:46 -0700 Subject: [PATCH 16/39] merge fields --- evm/src/extension_tower.rs | 195 ++++++++++++++++++++++++++++--------- 1 file changed, 149 insertions(+), 46 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 3a8d7443..c95cb24e 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1,6 +1,6 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; -use ethereum_types::U512; +use ethereum_types::{U256, U512}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -18,6 +18,102 @@ pub trait FieldExt: 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, @@ -30,77 +126,77 @@ pub const BLS_BASE: U512 = U512([ ]); #[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp381 { +pub struct BLS381 { pub val: U512, } -impl Fp381 { - pub fn new(val: usize) -> Fp381 { - Fp381 { +impl BLS381 { + pub fn new(val: usize) -> BLS381 { + BLS381 { val: U512::from(val), } } } -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Fp381 { +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> BLS381 { let xs = rng.gen::<[u64; 8]>(); - Fp381 { + BLS381 { val: U512(xs) % BLS_BASE, } } } -impl Add for Fp381 { +impl Add for BLS381 { type Output = Self; fn add(self, other: Self) -> Self { - Fp381 { + BLS381 { val: (self.val + other.val) % BLS_BASE, } } } -impl Neg for Fp381 { +impl Neg for BLS381 { type Output = Self; fn neg(self) -> Self::Output { - Fp381 { + BLS381 { val: (BLS_BASE - self.val) % BLS_BASE, } } } -impl Sub for Fp381 { +impl Sub for BLS381 { type Output = Self; fn sub(self, other: Self) -> Self { - Fp381 { + BLS381 { val: (BLS_BASE + self.val - other.val) % BLS_BASE, } } } -impl Fp381 { - fn lsh_128(self) -> Fp381 { +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 - Fp381 { + BLS381 { val: self.val.saturating_mul(b128) % BLS_BASE, } } - fn lsh_256(self) -> Fp381 { + fn lsh_256(self) -> BLS381 { self.lsh_128().lsh_128() } - fn lsh_512(self) -> Fp381 { + fn lsh_512(self) -> BLS381 { self.lsh_256().lsh_256() } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for Fp381 { +impl Mul for BLS381 { type Output = Self; fn mul(self, other: Self) -> Self { @@ -110,16 +206,16 @@ impl Mul for Fp381 { let y0 = U512(other.val.0[..4].try_into().unwrap()); let y1 = U512(other.val.0[4..].try_into().unwrap()); - let z00 = Fp381 { + let z00 = BLS381 { val: x0.saturating_mul(y0) % BLS_BASE, }; - let z01 = Fp381 { + let z01 = BLS381 { val: x0.saturating_mul(y1), }; - let z10 = Fp381 { + let z10 = BLS381 { val: x1.saturating_mul(y0), }; - let z11 = Fp381 { + let z11 = BLS381 { val: x1.saturating_mul(y1), }; @@ -127,16 +223,26 @@ impl Mul for Fp381 { } } -impl FieldExt for Fp381 { - const ZERO: Self = Fp381 { val: U512::zero() }; - const UNIT: Self = Fp381 { val: U512::one() }; - fn inv(self) -> Fp381 { - exp_fp(self, BLS_BASE - 2) +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 Fp381 { +impl Div for BLS381 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { @@ -144,19 +250,6 @@ impl Div for Fp381 { } } -fn exp_fp(x: Fp381, e: U512) -> Fp381 { - let mut current = x; - let mut product = Fp381 { val: U512::one() }; - - for j in 0..512 { - 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)] @@ -276,13 +369,23 @@ impl Div for Fp2 { } } +/// Helper function which multiplies by the Fp2 element +/// whose cube root we will adjoin in the next extension pub trait Adj { fn mul_adj(self) -> Self; } -/// Helper function which multiplies by the Fp2 element -/// whose cube root we will adjoin in the next extension -impl Adj for Fp2 { +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, + } + } +} + +impl Adj for Fp2 { fn mul_adj(self) -> Self { Fp2 { re: self.re - self.im, From 4d83c58d1f801ddd807292f32ddc296972f5c972 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 13:51:11 -0700 Subject: [PATCH 17/39] frob works --- evm/src/cpu/kernel/tests/bn254.rs | 403 +++++++++++++-------------- evm/src/extension_tower.rs | 440 ++++++++++++++++++++++++++++-- 2 files changed, 617 insertions(+), 226 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs index badfb472..7195fc73 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -2,13 +2,14 @@ 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::bn254_arithmetic::{Fp, Fp12, Fp2, Fp6}; +// use crate::bn254_pairing::{ +// gen_fp12_sparse, invariant_exponent, miller_loop, tate, Curve, TwistedCurve, +// }; +use crate::extension_tower::{Fp12, Stack, BN254}; use crate::memory::segments::Segment::BnPairing; fn extract_stack(interpreter: Interpreter<'static>) -> Vec { @@ -20,48 +21,48 @@ fn extract_stack(interpreter: Interpreter<'static>) -> Vec { .collect::>() } -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()); - } - stack.push(U256::from(0xdeadbeefu32)); - InterpreterMemoryInitialization { - label: label.to_string(), - stack, - segment: BnPairing, - memory: vec![], - } -} +// 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()); +// } +// stack.push(U256::from(0xdeadbeefu32)); +// InterpreterMemoryInitialization { +// label: label.to_string(), +// stack, +// segment: BnPairing, +// memory: vec![], +// } +// } -#[test] -fn test_mul_fp6() -> Result<()> { - let mut rng = rand::thread_rng(); - let f: Fp6 = rng.gen::(); - let g: Fp6 = rng.gen::(); +// #[test] +// fn test_mul_fp6() -> Result<()> { +// let mut rng = rand::thread_rng(); +// 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"); +// 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"); - let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); - let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); +// let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); +// let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); - let out_normal: Vec = extract_stack(intrptr_normal); - let out_square: Vec = extract_stack(intrptr_square); +// let out_normal: Vec = extract_stack(intrptr_normal); +// let out_square: Vec = extract_stack(intrptr_square); - let exp_normal: Vec = (f * g).on_stack(); - let exp_square: Vec = (f * f).on_stack(); +// let exp_normal: Vec = (f * g).on_stack(); +// let exp_square: Vec = (f * f).on_stack(); - assert_eq!(out_normal, exp_normal); - assert_eq!(out_square, exp_square); +// assert_eq!(out_normal, exp_normal); +// assert_eq!(out_square, exp_square); - Ok(()) -} +// Ok(()) +// } fn setup_mul_fp12_test( out: usize, - f: Fp12, - g: Fp12, + f: Fp12, + g: Fp12, label: &str, ) -> InterpreterMemoryInitialization { let in0: usize = 200; @@ -89,60 +90,60 @@ 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"); - let setup_sparse: InterpreterMemoryInitialization = - setup_mul_fp12_test(out, f, h, "mul_fp254_12_sparse"); + // let setup_sparse: InterpreterMemoryInitialization = + // setup_mul_fp12_test(out, f, h, "mul_fp254_12_sparse"); let setup_square: InterpreterMemoryInitialization = setup_mul_fp12_test(out, f, f, "square_fp254_12"); let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); - let intrptr_sparse: Interpreter = run_interpreter_with_memory(setup_sparse).unwrap(); + // let intrptr_sparse: Interpreter = run_interpreter_with_memory(setup_sparse).unwrap(); let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); let out_normal: Vec = intrptr_normal.extract_kernel_memory(BnPairing, out..out + 12); - let out_sparse: Vec = intrptr_sparse.extract_kernel_memory(BnPairing, out..out + 12); + // let out_sparse: Vec = intrptr_sparse.extract_kernel_memory(BnPairing, out..out + 12); let out_square: Vec = intrptr_square.extract_kernel_memory(BnPairing, out..out + 12); let exp_normal: Vec = (f * g).on_stack(); - let exp_sparse: Vec = (f * h).on_stack(); + // let exp_sparse: Vec = (f * h).on_stack(); let exp_square: Vec = (f * f).on_stack(); assert_eq!(out_normal, exp_normal); - assert_eq!(out_sparse, exp_sparse); + // assert_eq!(out_sparse, exp_sparse); assert_eq!(out_square, exp_square); Ok(()) } -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(), - segment: BnPairing, - memory: vec![], - } -} +// 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(), +// segment: BnPairing, +// memory: vec![], +// } +// } -#[test] -fn test_frob_fp6() -> Result<()> { - let mut rng = rand::thread_rng(); - 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(); - let out_frob: Vec = extract_stack(intrptr_frob); - let exp_frob: Vec = f.frob(n).on_stack(); - assert_eq!(out_frob, exp_frob); - } - Ok(()) -} +// #[test] +// fn test_frob_fp6() -> Result<()> { +// let mut rng = rand::thread_rng(); +// 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(); +// let out_frob: Vec = extract_stack(intrptr_frob); +// let exp_frob: Vec = f.frob(n).on_stack(); +// assert_eq!(out_frob, exp_frob); +// } +// 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 +156,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(); @@ -166,154 +167,154 @@ fn test_frob_fp12() -> Result<()> { Ok(()) } -#[test] -fn test_inv_fp12() -> Result<()> { - let ptr: usize = 200; - let inv: usize = 212; - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); +// #[test] +// 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 setup = InterpreterMemoryInitialization { - label: "inv_fp254_12".to_string(), - stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, f.on_stack())], - }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); - let expected: Vec = f.inv().on_stack(); +// let setup = InterpreterMemoryInitialization { +// label: "inv_fp254_12".to_string(), +// stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)], +// segment: BnPairing, +// memory: vec![(ptr, f.on_stack())], +// }; +// let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); +// let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); +// let expected: Vec = f.inv().on_stack(); - assert_eq!(output, expected); +// assert_eq!(output, expected); - Ok(()) -} +// Ok(()) +// } -#[test] -fn test_invariant_exponent() -> Result<()> { - let ptr: usize = 200; - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::(); +// #[test] +// fn test_invariant_exponent() -> Result<()> { +// let ptr: usize = 200; +// let mut rng = rand::thread_rng(); +// let f: Fp12 = rng.gen::(); - let setup = InterpreterMemoryInitialization { - label: "bn254_invariant_exponent".to_string(), - stack: vec![U256::from(ptr), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, f.on_stack())], - }; +// let setup = InterpreterMemoryInitialization { +// label: "bn254_invariant_exponent".to_string(), +// stack: vec![U256::from(ptr), U256::from(0xdeadbeefu32)], +// segment: BnPairing, +// memory: vec![(ptr, f.on_stack())], +// }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); - let expected: Vec = invariant_exponent(f).on_stack(); +// let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); +// let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); +// let expected: Vec = invariant_exponent(f).on_stack(); - assert_eq!(output, expected); +// assert_eq!(output, expected); - Ok(()) -} +// Ok(()) +// } -// The curve is cyclic with generator (1, 2) -pub const CURVE_GENERATOR: Curve = { - Curve { - x: Fp { val: U256::one() }, - y: Fp { - val: U256([2, 0, 0, 0]), - }, - } -}; +// // The curve is cyclic with generator (1, 2) +// pub const CURVE_GENERATOR: Curve = { +// Curve { +// x: Fp { val: U256::one() }, +// y: Fp { +// val: U256([2, 0, 0, 0]), +// }, +// } +// }; -// The twisted curve is cyclic with generator (x, y) as follows -pub const TWISTED_GENERATOR: TwistedCurve = { - TwistedCurve { - x: Fp2 { - re: Fp { - val: U256([ - 0x46debd5cd992f6ed, - 0x674322d4f75edadd, - 0x426a00665e5c4479, - 0x1800deef121f1e76, - ]), - }, - im: Fp { - val: U256([ - 0x97e485b7aef312c2, - 0xf1aa493335a9e712, - 0x7260bfb731fb5d25, - 0x198e9393920d483a, - ]), - }, - }, - y: Fp2 { - re: Fp { - val: U256([ - 0x4ce6cc0166fa7daa, - 0xe3d1e7690c43d37b, - 0x4aab71808dcb408f, - 0x12c85ea5db8c6deb, - ]), - }, - im: Fp { - val: U256([ - 0x55acdadcd122975b, - 0xbc4b313370b38ef3, - 0xec9e99ad690c3395, - 0x090689d0585ff075, - ]), - }, - }, - } -}; +// // The twisted curve is cyclic with generator (x, y) as follows +// pub const TWISTED_GENERATOR: TwistedCurve = { +// TwistedCurve { +// x: Fp2 { +// re: Fp { +// val: U256([ +// 0x46debd5cd992f6ed, +// 0x674322d4f75edadd, +// 0x426a00665e5c4479, +// 0x1800deef121f1e76, +// ]), +// }, +// im: Fp { +// val: U256([ +// 0x97e485b7aef312c2, +// 0xf1aa493335a9e712, +// 0x7260bfb731fb5d25, +// 0x198e9393920d483a, +// ]), +// }, +// }, +// y: Fp2 { +// re: Fp { +// val: U256([ +// 0x4ce6cc0166fa7daa, +// 0xe3d1e7690c43d37b, +// 0x4aab71808dcb408f, +// 0x12c85ea5db8c6deb, +// ]), +// }, +// im: Fp { +// val: U256([ +// 0x55acdadcd122975b, +// 0xbc4b313370b38ef3, +// 0xec9e99ad690c3395, +// 0x090689d0585ff075, +// ]), +// }, +// }, +// } +// }; -#[test] -fn test_miller() -> Result<()> { - let ptr: usize = 200; - let out: usize = 206; - let inputs: Vec = vec![ - CURVE_GENERATOR.x.val, - CURVE_GENERATOR.y.val, - TWISTED_GENERATOR.x.re.val, - TWISTED_GENERATOR.x.im.val, - TWISTED_GENERATOR.y.re.val, - TWISTED_GENERATOR.y.im.val, - ]; +// #[test] +// fn test_miller() -> Result<()> { +// let ptr: usize = 200; +// let out: usize = 206; +// let inputs: Vec = vec![ +// CURVE_GENERATOR.x.val, +// CURVE_GENERATOR.y.val, +// TWISTED_GENERATOR.x.re.val, +// TWISTED_GENERATOR.x.im.val, +// TWISTED_GENERATOR.y.re.val, +// TWISTED_GENERATOR.y.im.val, +// ]; - let setup = InterpreterMemoryInitialization { - label: "bn254_miller".to_string(), - stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, inputs)], - }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); - let expected = miller_loop(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); +// let setup = InterpreterMemoryInitialization { +// label: "bn254_miller".to_string(), +// stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], +// segment: BnPairing, +// memory: vec![(ptr, inputs)], +// }; +// let interpreter = run_interpreter_with_memory(setup).unwrap(); +// let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); +// let expected = miller_loop(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); - assert_eq!(output, expected); +// assert_eq!(output, expected); - Ok(()) -} +// Ok(()) +// } -#[test] -fn test_tate() -> Result<()> { - let ptr: usize = 200; - let out: usize = 206; - let inputs: Vec = vec![ - CURVE_GENERATOR.x.val, - CURVE_GENERATOR.y.val, - TWISTED_GENERATOR.x.re.val, - TWISTED_GENERATOR.x.im.val, - TWISTED_GENERATOR.y.re.val, - TWISTED_GENERATOR.y.im.val, - ]; +// #[test] +// fn test_tate() -> Result<()> { +// let ptr: usize = 200; +// let out: usize = 206; +// let inputs: Vec = vec![ +// CURVE_GENERATOR.x.val, +// CURVE_GENERATOR.y.val, +// TWISTED_GENERATOR.x.re.val, +// TWISTED_GENERATOR.x.im.val, +// TWISTED_GENERATOR.y.re.val, +// TWISTED_GENERATOR.y.im.val, +// ]; - let setup = InterpreterMemoryInitialization { - label: "bn254_tate".to_string(), - stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, inputs)], - }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); - let expected = tate(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); +// let setup = InterpreterMemoryInitialization { +// label: "bn254_tate".to_string(), +// stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], +// segment: BnPairing, +// memory: vec![(ptr, inputs)], +// }; +// let interpreter = run_interpreter_with_memory(setup).unwrap(); +// let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); +// let expected = tate(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); - assert_eq!(output, expected); +// assert_eq!(output, expected); - Ok(()) -} +// Ok(()) +// } diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index c95cb24e..15d21907 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1,3 +1,4 @@ +use std::mem::transmute; use std::ops::{Add, Div, Mul, Neg, Sub}; use ethereum_types::{U256, U512}; @@ -250,7 +251,7 @@ impl Div for BLS381 { } } -/// The degree 2 field extension Fp2 is given by adjoining i, the square root of -1, to Fp +/// 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 @@ -263,7 +264,7 @@ where impl Distribution> for Standard where - T: Distribution + FieldExt, + T: FieldExt, Standard: Distribution, { fn sample(&self, rng: &mut R) -> Fp2 { @@ -317,7 +318,7 @@ impl Mul for Fp2 { } impl Fp2 { - /// This function scalar multiplies an Fp2 by an Fp + /// This function scalar multiplies an Fp2 by an BN254 pub fn scale(self, x: T) -> Self { Fp2 { re: x * self.re, @@ -371,8 +372,10 @@ impl Div for Fp2 { /// Helper function which multiplies by the Fp2 element /// whose cube root we will adjoin in the next extension -pub trait Adj { +pub trait Adj: Sized { fn mul_adj(self) -> Self; + const FROB_T: [[Self; 6]; 2]; + const FROB_Z: [Self; 12]; } impl Adj for Fp2 { @@ -383,6 +386,393 @@ impl Adj for Fp2 { 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 { @@ -392,6 +782,8 @@ impl Adj for Fp2 { 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 @@ -409,7 +801,7 @@ where impl Distribution> for Standard where - T: Distribution + FieldExt, + T: FieldExt, Fp2: Adj, Standard: Distribution, { @@ -514,15 +906,6 @@ where } } -impl Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; - const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; -} - impl Fp6 where T: FieldExt, @@ -538,8 +921,8 @@ where /// 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 = Fp6::::FROB_T[0][n]; - let frob_t2 = Fp6::::FROB_T[1][n]; + let frob_t1 = Fp2::::FROB_T[0][n]; + let frob_t2 = Fp2::::FROB_T[1][n]; if n % 2 != 0 { Fp6 { @@ -579,7 +962,7 @@ where /// (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 + /// 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 @@ -612,8 +995,8 @@ where } } -/// 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 +/// 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 @@ -641,7 +1024,7 @@ where /// 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 + /// 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() @@ -665,7 +1048,7 @@ where impl Distribution> for Standard where - T: Distribution + FieldExt, + T: FieldExt, Fp2: Adj, Standard: Distribution, { @@ -773,7 +1156,7 @@ where let n = n % 12; Fp12 { z0: self.z0.frob(n), - z1: self.z1.frob(n).scale(Fp6::::FROB_Z[n]), + z1: self.z1.frob(n).scale(Fp2::::FROB_Z[n]), } } } @@ -791,6 +1174,13 @@ where } } -// trait Stack { -// fn on_stack(self) -> Vec; -// } +pub trait Stack { + fn on_stack(self) -> Vec; +} + +impl Stack for Fp12 { + fn on_stack(self) -> Vec { + let f: [U256; 12] = unsafe { transmute(self) }; + f.into_iter().collect() + } +} From fc72ce46c56986772ac84d4a6b7be6a910978421 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 13:55:51 -0700 Subject: [PATCH 18/39] fp6 works --- evm/src/cpu/kernel/tests/bn254.rs | 108 ++++++++++++++++-------------- evm/src/extension_tower.rs | 7 ++ 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs index 7195fc73..1840a3c2 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -9,7 +9,7 @@ use crate::cpu::kernel::interpreter::{ // use crate::bn254_pairing::{ // gen_fp12_sparse, invariant_exponent, miller_loop, tate, Curve, TwistedCurve, // }; -use crate::extension_tower::{Fp12, Stack, BN254}; +use crate::extension_tower::{Fp12, Fp6, Stack, BN254}; use crate::memory::segments::Segment::BnPairing; fn extract_stack(interpreter: Interpreter<'static>) -> Vec { @@ -21,43 +21,47 @@ fn extract_stack(interpreter: Interpreter<'static>) -> Vec { .collect::>() } -// 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()); -// } -// stack.push(U256::from(0xdeadbeefu32)); -// InterpreterMemoryInitialization { -// label: label.to_string(), -// stack, -// segment: BnPairing, -// memory: vec![], -// } -// } +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()); + } + stack.push(U256::from(0xdeadbeefu32)); + InterpreterMemoryInitialization { + label: label.to_string(), + stack, + segment: BnPairing, + memory: vec![], + } +} -// #[test] -// fn test_mul_fp6() -> Result<()> { -// let mut rng = rand::thread_rng(); -// let f: Fp6 = rng.gen::(); -// let g: Fp6 = rng.gen::(); +#[test] +fn test_mul_fp6() -> Result<()> { + let mut rng = rand::thread_rng(); + 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"); + 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"); -// let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); -// let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); + let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); + let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); -// let out_normal: Vec = extract_stack(intrptr_normal); -// let out_square: Vec = extract_stack(intrptr_square); + let out_normal: Vec = extract_stack(intrptr_normal); + let out_square: Vec = extract_stack(intrptr_square); -// let exp_normal: Vec = (f * g).on_stack(); -// let exp_square: Vec = (f * f).on_stack(); + let exp_normal: Vec = (f * g).on_stack(); + let exp_square: Vec = (f * f).on_stack(); -// assert_eq!(out_normal, exp_normal); -// assert_eq!(out_square, exp_square); + assert_eq!(out_normal, exp_normal); + assert_eq!(out_square, exp_square); -// Ok(()) -// } + Ok(()) +} fn setup_mul_fp12_test( out: usize, @@ -120,28 +124,28 @@ fn test_mul_fp12() -> Result<()> { Ok(()) } -// 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(), -// segment: BnPairing, -// memory: vec![], -// } -// } +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(), + segment: BnPairing, + memory: vec![], + } +} -// #[test] -// fn test_frob_fp6() -> Result<()> { -// let mut rng = rand::thread_rng(); -// 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(); -// let out_frob: Vec = extract_stack(intrptr_frob); -// let exp_frob: Vec = f.frob(n).on_stack(); -// assert_eq!(out_frob, exp_frob); -// } -// Ok(()) -// } +#[test] +fn test_frob_fp6() -> Result<()> { + let mut rng = rand::thread_rng(); + 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(); + let out_frob: Vec = extract_stack(intrptr_frob); + let exp_frob: Vec = f.frob(n).on_stack(); + assert_eq!(out_frob, exp_frob); + } + Ok(()) +} fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryInitialization { InterpreterMemoryInitialization { diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 15d21907..00e396b4 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1178,6 +1178,13 @@ 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) }; From 143225f449c8365b3cc09bc4b16a4477c3928dfc Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 19:19:02 -0700 Subject: [PATCH 19/39] finish --- evm/src/arithmetic/mod.rs | 2 +- evm/src/bn254_arithmetic.rs | 876 ----------------------------- evm/src/bn254_pairing.rs | 62 +- evm/src/cpu/kernel/interpreter.rs | 2 +- evm/src/cpu/kernel/tests/bn254.rs | 289 +++++----- evm/src/generation/prover_input.rs | 4 +- evm/src/lib.rs | 1 - 7 files changed, 181 insertions(+), 1055 deletions(-) delete mode 100644 evm/src/bn254_arithmetic.rs 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..08eb614c 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,14 +66,14 @@ 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.scale(cx), q.y.scale(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)) @@ -81,27 +81,31 @@ pub fn cord(p1: Curve, p2: Curve, q: TwistedCurve) -> Fp12 { /// 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 1840a3c2..8e71ffd6 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -2,14 +2,13 @@ use anyhow::Result; use ethereum_types::U256; use rand::Rng; +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::bn254_arithmetic::{Fp, Fp12, Fp2, Fp6}; -// use crate::bn254_pairing::{ -// gen_fp12_sparse, invariant_exponent, miller_loop, tate, Curve, TwistedCurve, -// }; -use crate::extension_tower::{Fp12, Fp6, Stack, BN254}; +use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254}; use crate::memory::segments::Segment::BnPairing; fn extract_stack(interpreter: Interpreter<'static>) -> Vec { @@ -96,29 +95,29 @@ fn test_mul_fp12() -> Result<()> { 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 h: Fp12 = gen_fp12_sparse(&mut rng); let setup_normal: InterpreterMemoryInitialization = setup_mul_fp12_test(out, f, g, "mul_fp254_12"); - // let setup_sparse: InterpreterMemoryInitialization = - // setup_mul_fp12_test(out, f, h, "mul_fp254_12_sparse"); + let setup_sparse: InterpreterMemoryInitialization = + setup_mul_fp12_test(out, f, h, "mul_fp254_12_sparse"); let setup_square: InterpreterMemoryInitialization = setup_mul_fp12_test(out, f, f, "square_fp254_12"); let intrptr_normal: Interpreter = run_interpreter_with_memory(setup_normal).unwrap(); - // let intrptr_sparse: Interpreter = run_interpreter_with_memory(setup_sparse).unwrap(); + let intrptr_sparse: Interpreter = run_interpreter_with_memory(setup_sparse).unwrap(); let intrptr_square: Interpreter = run_interpreter_with_memory(setup_square).unwrap(); let out_normal: Vec = intrptr_normal.extract_kernel_memory(BnPairing, out..out + 12); - // let out_sparse: Vec = intrptr_sparse.extract_kernel_memory(BnPairing, out..out + 12); + let out_sparse: Vec = intrptr_sparse.extract_kernel_memory(BnPairing, out..out + 12); let out_square: Vec = intrptr_square.extract_kernel_memory(BnPairing, out..out + 12); let exp_normal: Vec = (f * g).on_stack(); - // let exp_sparse: Vec = (f * h).on_stack(); + let exp_sparse: Vec = (f * h).on_stack(); let exp_square: Vec = (f * f).on_stack(); assert_eq!(out_normal, exp_normal); - // assert_eq!(out_sparse, exp_sparse); + assert_eq!(out_sparse, exp_sparse); assert_eq!(out_square, exp_square); Ok(()) @@ -171,154 +170,154 @@ fn test_frob_fp12() -> Result<()> { Ok(()) } -// #[test] -// fn test_inv_fp12() -> Result<()> { -// let ptr: usize = 200; -// let inv: usize = 212; -// let mut rng = rand::thread_rng(); -// let f: Fp12 = rng.gen::(); +#[test] +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 setup = InterpreterMemoryInitialization { -// label: "inv_fp254_12".to_string(), -// stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)], -// segment: BnPairing, -// memory: vec![(ptr, f.on_stack())], -// }; -// let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); -// let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); -// let expected: Vec = f.inv().on_stack(); + let setup = InterpreterMemoryInitialization { + label: "inv_fp254_12".to_string(), + stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)], + segment: BnPairing, + memory: vec![(ptr, f.on_stack())], + }; + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); + let expected: Vec = f.inv().on_stack(); -// assert_eq!(output, expected); + assert_eq!(output, expected); -// Ok(()) -// } + Ok(()) +} -// #[test] -// fn test_invariant_exponent() -> Result<()> { -// let ptr: usize = 200; -// let mut rng = rand::thread_rng(); -// let f: Fp12 = rng.gen::(); +#[test] +fn test_invariant_exponent() -> Result<()> { + let ptr: usize = 200; + let mut rng = rand::thread_rng(); + let f: Fp12 = rng.gen::>(); -// let setup = InterpreterMemoryInitialization { -// label: "bn254_invariant_exponent".to_string(), -// stack: vec![U256::from(ptr), U256::from(0xdeadbeefu32)], -// segment: BnPairing, -// memory: vec![(ptr, f.on_stack())], -// }; + let setup = InterpreterMemoryInitialization { + label: "bn254_invariant_exponent".to_string(), + stack: vec![U256::from(ptr), U256::from(0xdeadbeefu32)], + segment: BnPairing, + memory: vec![(ptr, f.on_stack())], + }; -// let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); -// let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); -// let expected: Vec = invariant_exponent(f).on_stack(); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); + let expected: Vec = invariant_exponent(f).on_stack(); -// assert_eq!(output, expected); + assert_eq!(output, expected); -// Ok(()) -// } + Ok(()) +} -// // The curve is cyclic with generator (1, 2) -// pub const CURVE_GENERATOR: Curve = { -// Curve { -// x: Fp { val: U256::one() }, -// y: Fp { -// val: U256([2, 0, 0, 0]), -// }, -// } -// }; +// The curve is cyclic with generator (1, 2) +pub const CURVE_GENERATOR: Curve = { + Curve { + x: BN254 { val: U256::one() }, + y: BN254 { + val: U256([2, 0, 0, 0]), + }, + } +}; -// // The twisted curve is cyclic with generator (x, y) as follows -// pub const TWISTED_GENERATOR: TwistedCurve = { -// TwistedCurve { -// x: Fp2 { -// re: Fp { -// val: U256([ -// 0x46debd5cd992f6ed, -// 0x674322d4f75edadd, -// 0x426a00665e5c4479, -// 0x1800deef121f1e76, -// ]), -// }, -// im: Fp { -// val: U256([ -// 0x97e485b7aef312c2, -// 0xf1aa493335a9e712, -// 0x7260bfb731fb5d25, -// 0x198e9393920d483a, -// ]), -// }, -// }, -// y: Fp2 { -// re: Fp { -// val: U256([ -// 0x4ce6cc0166fa7daa, -// 0xe3d1e7690c43d37b, -// 0x4aab71808dcb408f, -// 0x12c85ea5db8c6deb, -// ]), -// }, -// im: Fp { -// val: U256([ -// 0x55acdadcd122975b, -// 0xbc4b313370b38ef3, -// 0xec9e99ad690c3395, -// 0x090689d0585ff075, -// ]), -// }, -// }, -// } -// }; +// The twisted curve is cyclic with generator (x, y) as follows +pub const TWISTED_GENERATOR: TwistedCurve = { + TwistedCurve { + x: Fp2 { + re: BN254 { + val: U256([ + 0x46debd5cd992f6ed, + 0x674322d4f75edadd, + 0x426a00665e5c4479, + 0x1800deef121f1e76, + ]), + }, + im: BN254 { + val: U256([ + 0x97e485b7aef312c2, + 0xf1aa493335a9e712, + 0x7260bfb731fb5d25, + 0x198e9393920d483a, + ]), + }, + }, + y: Fp2 { + re: BN254 { + val: U256([ + 0x4ce6cc0166fa7daa, + 0xe3d1e7690c43d37b, + 0x4aab71808dcb408f, + 0x12c85ea5db8c6deb, + ]), + }, + im: BN254 { + val: U256([ + 0x55acdadcd122975b, + 0xbc4b313370b38ef3, + 0xec9e99ad690c3395, + 0x090689d0585ff075, + ]), + }, + }, + } +}; -// #[test] -// fn test_miller() -> Result<()> { -// let ptr: usize = 200; -// let out: usize = 206; -// let inputs: Vec = vec![ -// CURVE_GENERATOR.x.val, -// CURVE_GENERATOR.y.val, -// TWISTED_GENERATOR.x.re.val, -// TWISTED_GENERATOR.x.im.val, -// TWISTED_GENERATOR.y.re.val, -// TWISTED_GENERATOR.y.im.val, -// ]; +#[test] +fn test_miller() -> Result<()> { + let ptr: usize = 200; + let out: usize = 206; + let inputs: Vec = vec![ + CURVE_GENERATOR.x.val, + CURVE_GENERATOR.y.val, + TWISTED_GENERATOR.x.re.val, + TWISTED_GENERATOR.x.im.val, + TWISTED_GENERATOR.y.re.val, + TWISTED_GENERATOR.y.im.val, + ]; -// let setup = InterpreterMemoryInitialization { -// label: "bn254_miller".to_string(), -// stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], -// segment: BnPairing, -// memory: vec![(ptr, inputs)], -// }; -// let interpreter = run_interpreter_with_memory(setup).unwrap(); -// let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); -// let expected = miller_loop(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); + let setup = InterpreterMemoryInitialization { + label: "bn254_miller".to_string(), + stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], + segment: BnPairing, + memory: vec![(ptr, inputs)], + }; + let interpreter = run_interpreter_with_memory(setup).unwrap(); + let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); + let expected = miller_loop(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); -// assert_eq!(output, expected); + assert_eq!(output, expected); -// Ok(()) -// } + Ok(()) +} -// #[test] -// fn test_tate() -> Result<()> { -// let ptr: usize = 200; -// let out: usize = 206; -// let inputs: Vec = vec![ -// CURVE_GENERATOR.x.val, -// CURVE_GENERATOR.y.val, -// TWISTED_GENERATOR.x.re.val, -// TWISTED_GENERATOR.x.im.val, -// TWISTED_GENERATOR.y.re.val, -// TWISTED_GENERATOR.y.im.val, -// ]; +#[test] +fn test_tate() -> Result<()> { + let ptr: usize = 200; + let out: usize = 206; + let inputs: Vec = vec![ + CURVE_GENERATOR.x.val, + CURVE_GENERATOR.y.val, + TWISTED_GENERATOR.x.re.val, + TWISTED_GENERATOR.x.im.val, + TWISTED_GENERATOR.y.re.val, + TWISTED_GENERATOR.y.im.val, + ]; -// let setup = InterpreterMemoryInitialization { -// label: "bn254_tate".to_string(), -// stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], -// segment: BnPairing, -// memory: vec![(ptr, inputs)], -// }; -// let interpreter = run_interpreter_with_memory(setup).unwrap(); -// let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); -// let expected = tate(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); + let setup = InterpreterMemoryInitialization { + label: "bn254_tate".to_string(), + stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], + segment: BnPairing, + memory: vec![(ptr, inputs)], + }; + let interpreter = run_interpreter_with_memory(setup).unwrap(); + let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); + let expected = tate(CURVE_GENERATOR, TWISTED_GENERATOR).on_stack(); -// assert_eq!(output, expected); + assert_eq!(output, expected); -// Ok(()) -// } + Ok(()) +} 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 fa48d8dc..dc2204df 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -8,7 +8,6 @@ pub mod all_stark; pub mod arithmetic; -pub mod bn254_arithmetic; pub mod bn254_pairing; pub mod config; pub mod constraint_consumer; From b847d16e13913717efa2f3a29de84c906c541420 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 19:25:28 -0700 Subject: [PATCH 20/39] redundancy --- evm/src/bls381_arithmetic.rs | 605 ----------------------------------- 1 file changed, 605 deletions(-) delete mode 100644 evm/src/bls381_arithmetic.rs diff --git a/evm/src/bls381_arithmetic.rs b/evm/src/bls381_arithmetic.rs deleted file mode 100644 index 4a9daedd..00000000 --- a/evm/src/bls381_arithmetic.rs +++ /dev/null @@ -1,605 +0,0 @@ -use std::ops::{Add, Div, Mul, Neg, Sub}; - -use ethereum_types::U512; - -// use rand::distributions::{Distribution, Standard}; -// use rand::Rng; - -pub trait FieldExt: - Sized - + 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 BLS_BASE: U512 = U512([ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - 0x0, - 0x0, -]); - -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Fp381 { - pub val: U512, -} - -impl Fp381 { - pub fn new(val: usize) -> Fp381 { - Fp381 { - val: U512::from(val), - } - } -} - -// impl Distribution for Standard { -// fn sample(&self, rng: &mut R) -> Fp381 { -// let xs = rng.gen::<[u64; 8]>(); -// Fp381 { -// val: U512(xs) % BLS_BASE, -// } -// } -// } - -impl Add for Fp381 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp381 { - val: (self.val + other.val) % BLS_BASE, - } - } -} - -impl Neg for Fp381 { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp381 { - val: (BLS_BASE - self.val) % BLS_BASE, - } - } -} - -impl Sub for Fp381 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp381 { - val: (BLS_BASE + self.val - other.val) % BLS_BASE, - } - } -} - -impl Fp381 { - fn lsh_128(self) -> Fp381 { - 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 - Fp381 { - val: self.val.saturating_mul(b128) % BLS_BASE, - } - } - - fn lsh_256(self) -> Fp381 { - self.lsh_128().lsh_128() - } - - fn lsh_512(self) -> Fp381 { - self.lsh_256().lsh_256() - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for Fp381 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - let b256: U512 = U512([0, 0, 0, 0, 1, 0, 0, 0]); - // x1, y1 are at most (q-1) // 2^256 < 2^125 - let (x1, x0) = self.val.div_mod(b256); - let (y1, y0) = other.val.div_mod(b256); - - let z00 = Fp381 { - val: x0.saturating_mul(y0) % BLS_BASE, - }; - let z01 = Fp381 { - val: x0.saturating_mul(y1), - }; - let z10 = Fp381 { - val: x1.saturating_mul(y0), - }; - let z11 = Fp381 { - val: x1.saturating_mul(y1), - }; - - z00 + (z01 + z10).lsh_256() + z11.lsh_512() - } -} - -impl FieldExt for Fp381 { - const ZERO: Self = Fp381 { val: U512::zero() }; - const UNIT: Self = Fp381 { val: U512::one() }; - fn inv(self) -> Fp381 { - exp_fp(self, BLS_BASE - 2) - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp381 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -fn exp_fp(x: Fp381, e: U512) -> Fp381 { - let mut current = x; - let mut product = Fp381 { val: U512::one() }; - - for j in 0..512 { - 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 -where - T: FieldExt, -{ - pub re: T, - pub im: T, -} - -// impl> Distribution> for Standard { -// 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, - } - } -} - -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, - } - } - - /// 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().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() - } -} - -pub trait Adj { - fn mul_adj(self) -> Self; -} - -/// 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, - } - } -} - -/// 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 { - 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).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 { - // 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, - } - } -} - -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 { - Fp6 { - t0: self.t2.mul_adj(), - t1: self.t0, - t2: self.t1, - } - } -} - -impl Fp6 { - const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; - const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; -} - -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 - /// 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 = Fp6::::FROB_T[0][n]; - let frob_t2 = Fp6::::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 { - 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 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 - 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) - } -} - -#[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 -where - T: FieldExt, -{ - pub z0: Fp6, - pub z1: Fp6, -} - -impl FieldExt for Fp12 { - 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 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 - 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) - } -} - -// impl> Distribution> for Standard { -// fn sample(&self, rng: &mut R) -> Fp12 { -// let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); -// Fp12 { z0, z1 } -// } -// } - -impl Add for Fp12 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp12 { - z0: self.z0 + other.z0, - z1: self.z1 + other.z1, - } - } -} - -impl Neg for Fp12 { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp12 { - z0: -self.z0, - z1: -self.z1, - } - } -} - -impl Sub for Fp12 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp12 { - z0: self.z0 - other.z0, - z1: self.z1 - other.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, - } - } -} - -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(Fp6::::FROB_Z[n]), - } - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp12 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -// trait Stack { -// fn on_stack(self) -> Vec; -// } From 1437affcfd51a08e0d1ca671d7d597d32994ec0d Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 19:26:06 -0700 Subject: [PATCH 21/39] fmt --- evm/src/cpu/kernel/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index dedb84da..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::extension_tower::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; From 9ea0ebd78f407376e26c8f467459e03e472b78f1 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 21:10:01 -0700 Subject: [PATCH 22/39] skeleton --- evm/src/cpu/kernel/asm/curve/bls381/util.asm | 41 ++++++++++ evm/src/extension_tower.rs | 8 ++ evm/src/generation/prover_input.rs | 79 +++++++++++++++++++- 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/evm/src/cpu/kernel/asm/curve/bls381/util.asm b/evm/src/cpu/kernel/asm/curve/bls381/util.asm index e69de29b..5f39d68a 100644 --- a/evm/src/cpu/kernel/asm/curve/bls381/util.asm +++ b/evm/src/cpu/kernel/asm/curve/bls381/util.asm @@ -0,0 +1,41 @@ +%macro add_fp381 + // stack: x0, x1, y0, y1 + PROVER_INPUT(sf::bls381_base::add_hi) + // stack: z1, x0, x1, y0, y1 + SWAP4 + // stack: y1, x0, x1, y0, z1 + PROVER_INPUT(sf::bls381_base::add_lo) + // stack: z0, y1, x0, x1, y0, z1 + SWAP4 + // stack: y0, y1, x0, x1, z0, z1 + %pop4 + // stack: z0, z1 +%endmacro + +%macro mul_fp381 + // stack: x0, x1, y0, y1 + PROVER_INPUT(sf::bls381_base::mul_hi) + // stack: z1, x0, x1, y0, y1 + SWAP4 + // stack: y1, x0, x1, y0, z1 + PROVER_INPUT(sf::bls381_base::mul_lo) + // stack: z0, y1, x0, x1, y0, z1 + SWAP4 + // stack: y0, y1, x0, x1, z0, z1 + %pop4 + // stack: z0, z1 +%endmacro + +%macro sub_fp381 + // stack: x0, x1, y0, y1 + PROVER_INPUT(sf::bls381_base::sub_hi) + // stack: z1, x0, x1, y0, y1 + SWAP4 + // stack: y1, x0, x1, y0, z1 + PROVER_INPUT(sf::bls381_base::sub_lo) + // stack: z0, y1, x0, x1, y0, z1 + SWAP4 + // stack: y0, y1, x0, x1, z0, z1 + %pop4 + // stack: z0, z1 +%endmacro diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 00e396b4..f72b16ac 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -137,6 +137,14 @@ impl BLS381 { val: U512::from(val), } } + + pub fn lo(self) -> U256 { + U256(self.val.0[..4].try_into().unwrap()) + } + + pub fn hi(self) -> U256 { + U256(self.val.0[4..].try_into().unwrap()) + } } impl Distribution for Standard { diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 7f62acda..69e2f63d 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -2,12 +2,12 @@ use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; -use ethereum_types::{BigEndianHash, H256, U256}; +use ethereum_types::{BigEndianHash, H256, U256, U512}; use plonky2::field::types::Field; -use crate::extension_tower::{FieldExt, Fp12, BN254}; +use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ - Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, + Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; use crate::generation::state::GenerationState; @@ -30,6 +30,7 @@ impl GenerationState { match input_fn.0[0].as_str() { "end_of_txns" => self.run_end_of_txns(), "ff" => self.run_ff(input_fn), + "sf" => self.run_sf(input_fn), "ffe" => self.run_ffe(input_fn), "mpt" => self.run_mpt(), "rlp" => self.run_rlp(), @@ -56,6 +57,24 @@ impl GenerationState { field.op(op, x) } + /// Special finite field operations. + fn run_sf(&self, input_fn: &ProverInputFn) -> U256 { + let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap(); + let op = FieldOp::from_str(input_fn.0[2].as_str()).unwrap(); + let ptr = stack_peek(self, 11 - n).expect("Empty stack").as_usize(); + let xs: [U256; 4] = match field { + Bn254Base => { + let mut xs: [U256; 4] = [U256::zero(); 4]; + for i in 0..4 { + xs[i] = kernel_peek(self, BnPairing, ptr + i); + } + xs + } + _ => todo!(), + }; + field.op(op, xs) + } + /// Finite field extension operations. fn run_ffe(&self, input_fn: &ProverInputFn) -> U256 { let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap(); @@ -126,6 +145,8 @@ impl GenerationState { } enum EvmField { + Bls381Base, + Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, @@ -142,6 +163,8 @@ impl FromStr for EvmField { fn from_str(s: &str) -> Result { Ok(match s { + "bls381_base" => Bls381Base, + "bls381_scalar" => Bls381Scalar, "bn254_base" => Bn254Base, "bn254_scalar" => Bn254Scalar, "secp256k1_base" => Secp256k1Base, @@ -166,6 +189,8 @@ impl FromStr for FieldOp { impl EvmField { fn order(&self) -> U256 { match self { + EvmField::Bls381Base => todo!(), + EvmField::Bls381Scalar => todo!(), EvmField::Bn254Base => { U256::from_str("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47") .unwrap() @@ -206,6 +231,54 @@ impl EvmField { modexp(x, q, n) } + fn add_lo(&self, inputs: [U256; 4]) -> U256 { + let [y1, x0, x1, y0] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } + BLS381 { val: y }; + z.lo() + } + + fn add_hi(&self, inputs: [U256; 4]) -> U256 { + let [x0, x1, y0, y1] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } + BLS381 { val: y }; + z.hi() + } + + fn mul_lo(&self, inputs: [U256; 4]) -> U256 { + let [y1, x0, x1, y0] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } * BLS381 { val: y }; + z.lo() + } + + fn mul_hi(&self, inputs: [U256; 4]) -> U256 { + let [x0, x1, y0, y1] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } * BLS381 { val: y }; + z.hi() + } + + fn sub_lo(&self, inputs: [U256; 4]) -> U256 { + let [y1, x0, x1, y0] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } - BLS381 { val: y }; + z.lo() + } + + fn sub_hi(&self, inputs: [U256; 4]) -> U256 { + let [x0, x1, y0, y1] = inputs; + let x = U512::from(x0) + (U512::from(x1) << 256); + let y = U512::from(y0) + (U512::from(y1) << 256); + let z = BLS381 { val: x } - BLS381 { val: y }; + z.hi() + } + fn field_extension_inverse(&self, n: usize, f: [U256; 12]) -> U256 { let f: Fp12 = unsafe { transmute(f) }; let f_inv: [U256; 12] = unsafe { transmute(f.inv()) }; From 392c29f41216eee628a467f58dd5194a8c1fce76 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 21:16:19 -0700 Subject: [PATCH 23/39] compiles --- evm/src/generation/prover_input.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 69e2f63d..e25d11b4 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -60,19 +60,25 @@ impl GenerationState { /// Special finite field operations. fn run_sf(&self, input_fn: &ProverInputFn) -> U256 { let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap(); - let op = FieldOp::from_str(input_fn.0[2].as_str()).unwrap(); - let ptr = stack_peek(self, 11 - n).expect("Empty stack").as_usize(); - let xs: [U256; 4] = match field { + let inputs: [U256; 4] = match field { Bn254Base => { - let mut xs: [U256; 4] = [U256::zero(); 4]; + let mut inputs: [U256; 4] = [U256::zero(); 4]; for i in 0..4 { - xs[i] = kernel_peek(self, BnPairing, ptr + i); + inputs[i] = stack_peek(self, i).expect("Empty stack"); } - xs + inputs } _ => todo!(), }; - field.op(op, xs) + match input_fn.0[2].as_str() { + "add_lo" => field.add_lo(inputs), + "add_hi" => field.add_hi(inputs), + "mul_lo" => field.mul_lo(inputs), + "mul_hi" => field.mul_hi(inputs), + "sub_lo" => field.sub_lo(inputs), + "sub_hi" => field.sub_hi(inputs), + _ => todo!(), + } } /// Finite field extension operations. From 0e8f6a2f681e94edfc6d520b062812659724c6ee Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 21:17:46 -0700 Subject: [PATCH 24/39] test skeleton --- evm/src/cpu/kernel/asm/curve/bls381/util.asm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/evm/src/cpu/kernel/asm/curve/bls381/util.asm b/evm/src/cpu/kernel/asm/curve/bls381/util.asm index 5f39d68a..96136914 100644 --- a/evm/src/cpu/kernel/asm/curve/bls381/util.asm +++ b/evm/src/cpu/kernel/asm/curve/bls381/util.asm @@ -39,3 +39,15 @@ %pop4 // stack: z0, z1 %endmacro + +global test_add_fp381: + %add_fp381 + %jump(0xdeadbeef) + +global test_mul_fp381: + %add_fp381 + %jump(0xdeadbeef) + +global test_sub_fp381: + %add_fp381 + %jump(0xdeadbeef) From 373062b2a36b8099d722c42a31eda993d99997ba Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 21:22:08 -0700 Subject: [PATCH 25/39] on stack --- evm/src/extension_tower.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index f72b16ac..a71e2892 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1186,6 +1186,12 @@ pub trait Stack { fn on_stack(self) -> Vec; } +impl Stack for BLS381 { + fn on_stack(self) -> Vec { + vec![self.lo(), self.hi()] + } +} + impl Stack for Fp6 { fn on_stack(self) -> Vec { let f: [U256; 6] = unsafe { transmute(self) }; From 84a0bcf893da4a2d041aae90b0de326145ff9b1e Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Tue, 21 Mar 2023 21:28:15 -0700 Subject: [PATCH 26/39] cleanup --- evm/src/extension_tower.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index a71e2892..4b52c414 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -210,10 +210,10 @@ impl Mul for BLS381 { 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 x0 = U512::from(self.lo()); + let x1 = U512::from(self.hi()); + let y0 = U512::from(other.lo()); + let y1 = U512::from(other.hi()); let z00 = BLS381 { val: x0.saturating_mul(y0) % BLS_BASE, From 1627a9a0d33bddd5086450e67e5e268e84d106c0 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 09:55:35 -0700 Subject: [PATCH 27/39] tests pass --- evm/src/cpu/kernel/aggregator.rs | 1 + evm/src/cpu/kernel/asm/curve/bls381/util.asm | 4 +- evm/src/cpu/kernel/tests/bls381.rs | 48 ++++++++++++++++++++ evm/src/cpu/kernel/tests/mod.rs | 1 + evm/src/generation/prover_input.rs | 2 +- 5 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 evm/src/cpu/kernel/tests/bls381.rs diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 89f11467..595edfa7 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -35,6 +35,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/core/terminate.asm"), include_str!("asm/core/transfer.asm"), include_str!("asm/core/util.asm"), + include_str!("asm/curve/bls381/util.asm"), include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), diff --git a/evm/src/cpu/kernel/asm/curve/bls381/util.asm b/evm/src/cpu/kernel/asm/curve/bls381/util.asm index 96136914..a4e846cb 100644 --- a/evm/src/cpu/kernel/asm/curve/bls381/util.asm +++ b/evm/src/cpu/kernel/asm/curve/bls381/util.asm @@ -45,9 +45,9 @@ global test_add_fp381: %jump(0xdeadbeef) global test_mul_fp381: - %add_fp381 + %mul_fp381 %jump(0xdeadbeef) global test_sub_fp381: - %add_fp381 + %sub_fp381 %jump(0xdeadbeef) diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs new file mode 100644 index 00000000..0a35ebbb --- /dev/null +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -0,0 +1,48 @@ +use anyhow::Result; +use ethereum_types::{U256, U512}; +use rand::Rng; + +use crate::cpu::kernel::interpreter::{ + run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, +}; +use crate::extension_tower::{Stack, BLS381}; +use crate::memory::segments::Segment::KernelGeneral; + +fn extract_stack(interpreter: Interpreter<'static>) -> Vec { + interpreter + .stack() + .iter() + .rev() + .cloned() + .collect::>() +} + +fn combine_u256s(hi: U256, lo: U256) -> U512 { + U512::from(lo) + (U512::from(hi) << 256) +} + +#[test] +fn test_bls_ops() -> Result<()> { + let mut rng = rand::thread_rng(); + let x: BLS381 = rng.gen::(); + let y: BLS381 = rng.gen::(); + + let mut stack = x.on_stack(); + stack.extend(y.on_stack()); + + let setup = InterpreterMemoryInitialization { + label: "test_mul_fp381".to_string(), + stack, + segment: KernelGeneral, + memory: vec![], + }; + let interpreter = run_interpreter_with_memory(setup).unwrap(); + let output = extract_stack(interpreter); + println!("{:#?}", output); + let output_512 = combine_u256s(output[1], output[0]); + let expected = x * y; + + assert_eq!(expected.val, output_512); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 061cd57d..68695e2f 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,6 +1,7 @@ mod account_code; mod balance; mod bignum; +mod bls381; mod bn254; mod core; mod ecc; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index e25d11b4..74321562 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -61,7 +61,7 @@ impl GenerationState { fn run_sf(&self, input_fn: &ProverInputFn) -> U256 { let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap(); let inputs: [U256; 4] = match field { - Bn254Base => { + Bls381Base => { let mut inputs: [U256; 4] = [U256::zero(); 4]; for i in 0..4 { inputs[i] = stack_peek(self, i).expect("Empty stack"); From 1f3e3de77c3cf9b965a32061beb153a07b6a4da9 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 10:16:02 -0700 Subject: [PATCH 28/39] clean and generalize --- evm/src/cpu/kernel/tests/bls381.rs | 46 +++++++++++++----------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs index 0a35ebbb..6d412849 100644 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -8,17 +8,20 @@ use crate::cpu::kernel::interpreter::{ use crate::extension_tower::{Stack, BLS381}; use crate::memory::segments::Segment::KernelGeneral; -fn extract_stack(interpreter: Interpreter<'static>) -> Vec { - interpreter - .stack() - .iter() - .rev() - .cloned() - .collect::>() -} - -fn combine_u256s(hi: U256, lo: U256) -> U512 { - U512::from(lo) + (U512::from(hi) << 256) +fn run_and_return_bls(label: String, x: BLS381, y: BLS381) -> BLS381 { + let mut stack = x.on_stack(); + stack.extend(y.on_stack()); + let setup = InterpreterMemoryInitialization { + label, + stack, + segment: KernelGeneral, + memory: vec![], + }; + let interpreter = run_interpreter_with_memory(setup).unwrap(); + let output = interpreter.stack(); + BLS381 { + val: U512::from(output[1]) + (U512::from(output[0]) << 256), + } } #[test] @@ -27,22 +30,13 @@ fn test_bls_ops() -> Result<()> { let x: BLS381 = rng.gen::(); let y: BLS381 = rng.gen::(); - let mut stack = x.on_stack(); - stack.extend(y.on_stack()); + let output_add = run_and_return_bls("test_add_fp381".to_string(), x, y); + let output_mul = run_and_return_bls("test_mul_fp381".to_string(), x, y); + let output_sub = run_and_return_bls("test_sub_fp381".to_string(), x, y); - let setup = InterpreterMemoryInitialization { - label: "test_mul_fp381".to_string(), - stack, - segment: KernelGeneral, - memory: vec![], - }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); - let output = extract_stack(interpreter); - println!("{:#?}", output); - let output_512 = combine_u256s(output[1], output[0]); - let expected = x * y; - - assert_eq!(expected.val, output_512); + assert_eq!(output_add, x + y); + assert_eq!(output_mul, x * y); + assert_eq!(output_sub, x - y); Ok(()) } From d0b2b81e11af606b45f5e7f5e9206b9ce11d72dc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 22 Mar 2023 18:43:41 +0100 Subject: [PATCH 29/39] More MemoryError (#932) --- evm/src/witness/operation.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 43576ff4..8ed0e551 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -11,7 +11,9 @@ use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; +use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; use crate::witness::errors::ProgramError; +use crate::witness::errors::ProgramError::MemoryError; use crate::witness::memory::{MemoryAddress, MemoryOp}; use crate::witness::util::{ keccak_sponge_log, mem_read_gp_with_log_and_fill, mem_write_gp_log_and_fill, @@ -632,9 +634,15 @@ pub(crate) fn generate_mstore_general( stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; let address = MemoryAddress { - context: context.as_usize(), - segment: segment.as_usize(), - virt: virt.as_usize(), + context: context + .try_into() + .map_err(|_| MemoryError(ContextTooLarge { context }))?, + segment: segment + .try_into() + .map_err(|_| MemoryError(SegmentTooLarge { segment }))?, + virt: virt + .try_into() + .map_err(|_| MemoryError(VirtTooLarge { virt }))?, }; let log_write = mem_write_gp_log_and_fill(4, address, state, &mut row, val); From 3425391a091e4ddaf4508e223bff84e91bbbef30 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 10:57:26 -0700 Subject: [PATCH 30/39] more comments --- evm/src/extension_tower.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 00e396b4..2d6ff6ed 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -370,8 +370,13 @@ impl Div for Fp2 { } } -/// Helper function which multiplies by the Fp2 element -/// whose cube root we will adjoin in the next extension +/// 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]; From 645ef664a2335e115fd17769d52e14461719afda Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 10:58:09 -0700 Subject: [PATCH 31/39] comment --- evm/src/extension_tower.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 2d6ff6ed..23c130a0 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -370,8 +370,8 @@ impl Div for Fp2 { } } -/// This trait defines the method which multiplies -/// by the Fp2 element t^3 whose cube root we will +/// 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, From 15bafce5ddc986bac343926c8d3db601a4d8a686 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 22 Mar 2023 19:14:17 +0100 Subject: [PATCH 32/39] Implement CREATE2 address generation (#936) * Implement create2 address gen * Clippy * Minor * Wrong order * Fix test * Fix comment --- evm/src/cpu/kernel/asm/core/create.asm | 6 +- .../cpu/kernel/asm/core/create_addresses.asm | 25 ++++-- .../cpu/kernel/tests/core/create_addresses.rs | 90 ++++++++++++++++--- 3 files changed, 102 insertions(+), 19 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index d7e6eed2..5360e616 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -47,10 +47,10 @@ global sys_create2: SWAP4 %stack (salt) -> (salt, sys_create2_got_address) // stack: salt, sys_create2_got_address, value, code_offset, code_len, kexit_info - DUP4 // code_len - DUP4 // code_offset + DUP5 // code_len + DUP5 // code_offset PUSH @SEGMENT_MAIN_MEMORY - PUSH 0 // context + GET_CONTEXT KECCAK_GENERAL // stack: hash, salt, sys_create2_got_address, value, code_offset, code_len, kexit_info %address diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 7dd4e889..d72d7e67 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -34,15 +34,28 @@ global get_create_address: // Computes the address for a contract based on the CREATE2 rule, i.e. // address = KEC(0xff || sender || salt || code_hash)[12:] -// -// Pre stack: sender, salt, code_hash, retdest +// Clobbers @SEGMENT_KERNEL_GENERAL. +// Pre stack: sender, code_hash, salt, retdest // Post stack: address global get_create2_address: - // stack: sender, salt, code_hash, retdest - // TODO: Replace with actual implementation. - %pop3 - PUSH 123 + // stack: sender, code_hash, salt, retdest + PUSH 0xff PUSH 0 %mstore_kernel_general + %stack (sender, code_hash, salt, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + %jump(mstore_unpacking) +get_create2_address_contd: + POP + %stack (salt, code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + %jump(mstore_unpacking) +get_create2_address_contd2: + POP + %stack (code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + %jump(mstore_unpacking) +get_create2_address_finish: + POP + %stack (retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 0, 85, retdest) // context, segment, offset, len + KECCAK_GENERAL // stack: address, retdest + %mod_const(0x10000000000000000000000000000000000000000) // 2^160 %observe_new_address SWAP1 JUMP diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs index 03d780d8..3f316578 100644 --- a/evm/src/cpu/kernel/tests/core/create_addresses.rs +++ b/evm/src/cpu/kernel/tests/core/create_addresses.rs @@ -1,6 +1,9 @@ +use std::str::FromStr; + use anyhow::Result; -use ethereum_types::U256; +use ethereum_types::{H256, U256}; use hex_literal::hex; +use keccak_hash::keccak; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -24,22 +27,89 @@ fn test_get_create_address() -> Result<()> { Ok(()) } +struct Create2TestCase { + code_hash: H256, + salt: U256, + sender: U256, + expected_addr: U256, +} + +/// Taken from https://eips.ethereum.org/EIPS/eip-1014 +fn create2_test_cases() -> Vec { + vec![ + Create2TestCase { + code_hash: keccak(hex!("00")), + salt: U256::zero(), + sender: U256::zero(), + expected_addr: U256::from_str("0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("00")), + salt: U256::zero(), + sender: U256::from_str("0xdeadbeef00000000000000000000000000000000").unwrap(), + expected_addr: U256::from_str("0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("00")), + salt: U256::from_str( + "0x000000000000000000000000feed000000000000000000000000000000000000", + ) + .unwrap(), + sender: U256::from_str("0xdeadbeef00000000000000000000000000000000").unwrap(), + expected_addr: U256::from_str("0xD04116cDd17beBE565EB2422F2497E06cC1C9833").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("deadbeef")), + salt: U256::zero(), + sender: U256::zero(), + expected_addr: U256::from_str("0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("deadbeef")), + salt: U256::from_str( + "0x00000000000000000000000000000000000000000000000000000000cafebabe", + ) + .unwrap(), + sender: U256::from_str("0x00000000000000000000000000000000deadbeef").unwrap(), + expected_addr: U256::from_str("0x60f3f640a8508fC6a86d45DF051962668E1e8AC7").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")), + salt: U256::from_str( + "0x00000000000000000000000000000000000000000000000000000000cafebabe", + ) + .unwrap(), + sender: U256::from_str("0x00000000000000000000000000000000deadbeef").unwrap(), + expected_addr: U256::from_str("0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C").unwrap(), + }, + Create2TestCase { + code_hash: keccak(hex!("")), + salt: U256::zero(), + sender: U256::zero(), + expected_addr: U256::from_str("0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0").unwrap(), + }, + ] +} + #[test] fn test_get_create2_address() -> Result<()> { let get_create2_address = KERNEL.global_labels["get_create2_address"]; - // TODO: Replace with real data once we have a real implementation. let retaddr = 0xdeadbeefu32.into(); - let code_hash = 0.into(); - let salt = 5.into(); - let sender = 0.into(); - let expected_addr = 123.into(); - let initial_stack = vec![retaddr, code_hash, salt, sender]; - let mut interpreter = Interpreter::new_with_kernel(get_create2_address, initial_stack); - interpreter.run()?; + for Create2TestCase { + code_hash, + salt, + sender, + expected_addr, + } in create2_test_cases() + { + let initial_stack = vec![retaddr, salt, U256::from(code_hash.0), sender]; + let mut interpreter = Interpreter::new_with_kernel(get_create2_address, initial_stack); + interpreter.run()?; - assert_eq!(interpreter.stack(), &[expected_addr]); + assert_eq!(interpreter.stack(), &[expected_addr]); + } Ok(()) } From f4e65feb65b74620f8c664ae82f8e68ee6f0370e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 22 Mar 2023 19:14:55 +0100 Subject: [PATCH 33/39] Fix bugs in `wcopy` and `update_mem_words` (#934) * Fix bugs in wcopy and update_mem_words * Update comment --- evm/src/cpu/kernel/asm/memory/metadata.asm | 6 +++++- evm/src/cpu/kernel/asm/memory/syscalls.asm | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index d891c0d1..1c526c2d 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -201,7 +201,7 @@ global sys_basefee: // stack: old_num_words, num_words, kexit_info DUP2 DUP2 GT // stack: old_num_words > num_words, old_num_words, num_words, kexit_info - %jumpi(%%end) + %jumpi(%%no_update) // stack: old_num_words, num_words, kexit_info %memory_cost // stack: old_cost, num_words, kexit_info @@ -214,6 +214,10 @@ global sys_basefee: SUB // stack: additional_cost, kexit_info %charge_gas + %jump(%%end) +%%no_update: + // stack: old_num_words, num_words, kexit_info + %pop2 %%end: // stack: kexit_info %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index 11f8054b..86435408 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -124,14 +124,14 @@ sys_calldataload_after_mload_packing: // stack: kexit_info, dest_offset, offset, size DUP4 %num_bytes_to_num_words %mul_const(@GAS_COPY) %add_const(@GAS_VERYLOW) %charge_gas - %stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, dest_offset, offset, size, kexit_info) + %stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, kexit_info, dest_offset, offset, size) ADD // TODO: check for overflow, see discussion here https://github.com/mir-protocol/plonky2/pull/930/files/a4ea0965d79561c345e2f77836c07949c7e0bc69#r1143630253 - // stack: expanded_num_bytes, dest_offset, offset, size, kexit_info + // stack: expanded_num_bytes, kexit_info, dest_offset, offset, size, kexit_info DUP1 %ensure_reasonable_offset %update_mem_bytes GET_CONTEXT - %stack (context, dest_offset, offset, size, kexit_info) -> + %stack (context, kexit_info, dest_offset, offset, size) -> (context, @SEGMENT_MAIN_MEMORY, dest_offset, context, $segment, offset, size, %%after, kexit_info) %jump(memcpy) %%after: From 74afec70ed340ac64b74b2ca16ef32e85b501bcd Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 11:56:58 -0700 Subject: [PATCH 34/39] remove imports --- evm/src/cpu/kernel/tests/bls381.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs index 6d412849..2986537f 100644 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -1,9 +1,9 @@ use anyhow::Result; -use ethereum_types::{U256, U512}; +use ethereum_types::{U512}; use rand::Rng; use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, + run_interpreter_with_memory, InterpreterMemoryInitialization, }; use crate::extension_tower::{Stack, BLS381}; use crate::memory::segments::Segment::KernelGeneral; From c8d2769c289c9aeeb3fcf1f9517a08a5257d37c4 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 11:58:51 -0700 Subject: [PATCH 35/39] fmt --- evm/src/cpu/kernel/tests/bls381.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs index 2986537f..afd22a14 100644 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use ethereum_types::{U512}; +use ethereum_types::U512; use rand::Rng; use crate::cpu::kernel::interpreter::{ From 0650d2636c337abece38cadb317c6fdee706f2b4 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Wed, 22 Mar 2023 17:26:14 -0700 Subject: [PATCH 36/39] remove .scale --- evm/src/bn254_pairing.rs | 4 +-- evm/src/extension_tower.rs | 69 ++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/evm/src/bn254_pairing.rs b/evm/src/bn254_pairing.rs index 08eb614c..7277c2a8 100644 --- a/evm/src/bn254_pairing.rs +++ b/evm/src/bn254_pairing.rs @@ -69,14 +69,14 @@ pub fn miller_loop(p: Curve, q: TwistedCurve) -> Fp12 { 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.scale(cx), q.y.scale(cy)) + 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 { 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. diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 23c130a0..35bbefdd 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -6,8 +6,7 @@ use rand::distributions::{Distribution, Standard}; use rand::Rng; pub trait FieldExt: - Sized - + Copy + Copy + std::ops::Add + std::ops::Neg + std::ops::Sub @@ -317,15 +316,19 @@ impl Mul for Fp2 { } } -impl Fp2 { - /// This function scalar multiplies an Fp2 by an BN254 - pub fn scale(self, x: T) -> Self { +/// This function scalar multiplies an Fp2 by an Fp +impl Mul for Fp2 { + type Output = Fp2; + + fn mul(self, other: T) -> Self { Fp2 { - re: x * self.re, - im: x * self.im, + 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 @@ -357,7 +360,7 @@ impl FieldExt for Fp2 { /// 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().scale(norm_sq.inv()) + self.conj() * norm_sq.inv() } } @@ -880,17 +883,19 @@ where } } -impl Fp6 +/// This function scalar multiplies an Fp6 by an Fp2 +impl Mul> for Fp6 where T: FieldExt, Fp2: Adj, { - // This function scalar multiplies an Fp6 by an Fp2 - fn scale(self, x: Fp2) -> Fp6 { + type Output = Fp6; + + fn mul(self, other: Fp2) -> Self { Fp6 { - t0: x * self.t0, - t1: x * self.t1, - t2: x * self.t2, + t0: other * self.t0, + t1: other * self.t1, + t2: other * self.t2, } } } @@ -981,9 +986,9 @@ where 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_odds_over_phi = prod_135 * phi.inv(); let prod_24 = prod_13.frob(1); - prod_24.scale(prod_odds_over_phi) + prod_24 * prod_odds_over_phi } } @@ -1044,10 +1049,10 @@ where 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_odds_over_phi = prod_odds * 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) + let prod_except_six = prod_evens_except_six * prod_odds_over_phi; + self.conj() * prod_except_six } } @@ -1126,19 +1131,27 @@ where } } +/// 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, { - // 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, @@ -1161,7 +1174,7 @@ where let n = n % 12; Fp12 { z0: self.z0.frob(n), - z1: self.z1.frob(n).scale(Fp2::::FROB_Z[n]), + z1: self.z1.frob(n) * (Fp2::::FROB_Z[n]), } } } From f9fa38d3cd9baac57be902e7a1fa32aec401b261 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 22 Mar 2023 21:53:33 -0700 Subject: [PATCH 37/39] Fix new account insert key --- evm/src/cpu/kernel/asm/core/process_txn.asm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index acce98f9..29b669be 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -81,6 +81,8 @@ global process_contract_creation_txn: // stack: account_ptr, address, retdest DUP2 // stack: address, account_ptr, address, retdest + %addr_to_state_key + // stack: state_key, account_ptr, address, retdest %mpt_insert_state_trie // stack: address, retdest From ce22d94518149f5b93a208e31c743979f0100eee Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 23 Mar 2023 20:22:25 +0100 Subject: [PATCH 38/39] Access lists (#937) * Working linear search * Working linear search for storage keys * Macros * Gas for wextaccount * Add origin and to to the access list * Handle precompiles * Fixes * Other opcodes * Access list SLOAD * Minor * Add TODO for SSTORE * Comments * Clippy * Minor * PR feedback: cold_access * PR feedback: simplify insert_accessed_addresses_no_return macro * Fix sys_selfdestruct * Store length in global metadata and store arrays in kernel memory * Truncate address in all *CALL syscalls --- evm/src/cpu/kernel/aggregator.rs | 1 + evm/src/cpu/kernel/asm/account_code.asm | 28 ++- evm/src/cpu/kernel/asm/balance.asm | 14 +- evm/src/cpu/kernel/asm/core/access_lists.asm | 102 +++++++++ evm/src/cpu/kernel/asm/core/call.asm | 24 +++ evm/src/cpu/kernel/asm/core/create.asm | 1 + evm/src/cpu/kernel/asm/core/process_txn.asm | 12 ++ evm/src/cpu/kernel/asm/core/terminate.asm | 7 +- .../kernel/asm/mpt/storage/storage_read.asm | 12 +- .../kernel/asm/mpt/storage/storage_write.asm | 2 + evm/src/cpu/kernel/asm/util/basic_macros.asm | 5 + .../cpu/kernel/constants/global_metadata.rs | 10 +- evm/src/cpu/kernel/constants/mod.rs | 20 +- evm/src/cpu/kernel/interpreter.rs | 1 + evm/src/cpu/kernel/tests/core/access_lists.rs | 195 ++++++++++++++++++ evm/src/cpu/kernel/tests/core/mod.rs | 1 + evm/src/memory/segments.rs | 12 +- 17 files changed, 437 insertions(+), 10 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/core/access_lists.asm create mode 100644 evm/src/cpu/kernel/tests/core/access_lists.rs diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 89f11467..6f45013d 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -35,6 +35,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/core/terminate.asm"), include_str!("asm/core/transfer.asm"), include_str!("asm/core/util.asm"), + include_str!("asm/core/access_lists.asm"), include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 10d0a686..8cbc3221 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -1,8 +1,18 @@ global sys_extcodehash: // stack: kexit_info, address - // TODO: Charge gas. - SWAP1 + SWAP1 %u256_to_addr // stack: address, kexit_info + DUP1 %insert_accessed_addresses + // stack: cold_access, address, kexit_info + PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS + MUL + PUSH @GAS_WARMACCESS + ADD + %stack (gas, address, kexit_info) -> (gas, kexit_info, address) + %charge_gas + // stack: kexit_info, address + + SWAP1 %extcodehash // stack: hash, kexit_info SWAP1 @@ -41,7 +51,18 @@ retzero: global sys_extcodesize: // stack: kexit_info, address - // TODO: Charge gas. + SWAP1 %u256_to_addr + // stack: address, kexit_info + DUP1 %insert_accessed_addresses + // stack: cold_access, address, kexit_info + PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS + MUL + PUSH @GAS_WARMACCESS + ADD + %stack (gas, address, kexit_info) -> (gas, kexit_info, address) + %charge_gas + // stack: kexit_info, address + SWAP1 // stack: address, kexit_info %extcodesize @@ -69,6 +90,7 @@ global sys_extcodecopy: // TODO: Charge other gas. %stack (kexit_info, address, dest_offset, offset, size) -> (address, dest_offset, offset, size, kexit_info) + %u256_to_addr DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. %extcodecopy // stack: kexit_info EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/balance.asm b/evm/src/cpu/kernel/asm/balance.asm index 1c6429d2..f175d027 100644 --- a/evm/src/cpu/kernel/asm/balance.asm +++ b/evm/src/cpu/kernel/asm/balance.asm @@ -1,7 +1,17 @@ global sys_balance: // stack: kexit_info, address - // TODO: assuming a cold account access for now. - %charge_gas_const(@GAS_COLDACCOUNTACCESS) + SWAP1 %u256_to_addr + // stack: address, kexit_info + DUP1 %insert_accessed_addresses + // stack: cold_access, address, kexit_info + PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS + MUL + PUSH @GAS_WARMACCESS + ADD + %stack (gas, address, kexit_info) -> (gas, kexit_info, address) + %charge_gas + // stack: kexit_info, address + SWAP1 // stack: address, kexit_info %balance diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm new file mode 100644 index 00000000..80d96e43 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -0,0 +1,102 @@ +/// Access lists for addresses and storage keys. +/// The access list is stored in an array. The length of the array is stored in the global metadata. +/// For storage keys, the address and key are stored as two consecutive elements. +/// The array is stored in the SEGMENT_ACCESSED_ADDRESSES segment for addresses and in the SEGMENT_ACCESSED_STORAGE_KEYS segment for storage keys. +/// Both arrays are stored in the kernel memory (context=0). +/// Searching and inserting is done by doing a linear search through the array. +/// If the address/storage key isn't found in the array, it is inserted at the end. +/// TODO: Look into using a more efficient data structure for the access lists. + +%macro insert_accessed_addresses + %stack (addr) -> (addr, %%after) + %jump(insert_accessed_addresses) +%%after: + // stack: cold_access +%endmacro + +%macro insert_accessed_addresses_no_return + %insert_accessed_addresses + POP +%endmacro + +/// Inserts the address into the access list if it is not already present. +/// Return 1 if the address was inserted, 0 if it was already present. +global insert_accessed_addresses: + // stack: addr, retdest + %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) + // stack: len, addr, retdest + PUSH 0 +insert_accessed_addresses_loop: + %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) + EQ %jumpi(insert_address) + // stack: i, len, addr, retdest + DUP1 %mload_kernel(@SEGMENT_ACCESSED_ADDRESSES) + // stack: loaded_addr, i, len, addr, retdest + DUP4 + // stack: addr, loaded_addr, i, len, addr, retdest + EQ %jumpi(insert_accessed_addresses_found) + // stack: i, len, addr, retdest + %increment + %jump(insert_accessed_addresses_loop) + +insert_address: + %stack (i, len, addr, retdest) -> (i, addr, len, retdest) + %mstore_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Store new address at the end of the array. + // stack: len, retdest + %increment + %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Store new length. + PUSH 1 // Return 1 to indicate that the address was inserted. + SWAP1 JUMP + +insert_accessed_addresses_found: + %stack (i, len, addr, retdest) -> (retdest, 0) // Return 0 to indicate that the address was already present. + JUMP + + +%macro insert_accessed_storage_keys + %stack (addr, key) -> (addr, key, %%after) + %jump(insert_accessed_storage_keys) +%%after: + // stack: cold_access +%endmacro + +/// Inserts the storage key into the access list if it is not already present. +/// Return 1 if the storage key was inserted, 0 if it was already present. +global insert_accessed_storage_keys: + // stack: addr, key, retdest + %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) + // stack: len, addr, key, retdest + PUSH 0 +insert_accessed_storage_keys_loop: + %stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest) + EQ %jumpi(insert_storage_key) + // stack: i, len, addr, key, retdest + DUP1 %increment %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + // stack: loaded_key, i, len, addr, key, retdest + DUP2 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + // stack: loaded_addr, loaded_key, i, len, addr, key, retdest + DUP5 EQ + // stack: loaded_addr==addr, loaded_key, i, len, addr, key, retdest + SWAP1 DUP6 EQ + // stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, retdest + MUL // AND + %jumpi(insert_accessed_storage_keys_found) + // stack: i, len, addr, key, retdest + %add_const(2) + %jump(insert_accessed_storage_keys_loop) + +insert_storage_key: + // stack: i, len, addr, key, retdest + DUP1 %increment + %stack (i_plus_1, i, len, addr, key, retdest) -> (i, addr, i_plus_1, key, i_plus_1, retdest) + %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new address at the end of the array. + %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new key after that + // stack: i_plus_1, retdest + %increment + %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length in front of the array. + PUSH 1 // Return 1 to indicate that the storage key was inserted. + SWAP1 JUMP + +insert_accessed_storage_keys_found: + %stack (i, len, addr, key, retdest) -> (retdest, 0) // Return 0 to indicate that the storage key was already present. + JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 103977b2..28f0f6e2 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -4,6 +4,12 @@ global sys_call: // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size // TODO: Charge gas. + SWAP2 + // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size + %u256_to_addr // Truncate to 160 bits + DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. + SWAP2 + // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size %create_context // stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size @@ -29,6 +35,12 @@ global sys_call: global sys_callcode: // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size // TODO: Charge gas. + SWAP2 + // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size + %u256_to_addr // Truncate to 160 bits + DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. + SWAP2 + // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size %create_context // stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size @@ -53,6 +65,12 @@ global sys_callcode: global sys_staticcall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size // TODO: Charge gas. + SWAP2 + // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size + %u256_to_addr // Truncate to 160 bits + DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. + SWAP2 + // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size %create_context // stack: new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size @@ -74,6 +92,12 @@ global sys_staticcall: global sys_delegatecall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size // TODO: Charge gas. + SWAP2 + // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size + %u256_to_addr // Truncate to 160 bits + DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. + SWAP2 + // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size %create_context // stack: new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index 5360e616..3157a262 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -73,6 +73,7 @@ sys_create2_finish: // Note: CODE_ADDR refers to a (context, segment, offset) tuple. global create_inner: // stack: address, sender, endowment, CODE_ADDR, code_len, retdest + DUP1 %insert_accessed_addresses_no_return %stack (address, sender, endowment) -> (sender, address, endowment, sender, address) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 29b669be..1c30d88a 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -129,13 +129,25 @@ global process_message_txn: // stack: retdest %mload_txn_field(@TXN_FIELD_VALUE) %mload_txn_field(@TXN_FIELD_TO) + DUP1 %insert_accessed_addresses_no_return %mload_txn_field(@TXN_FIELD_ORIGIN) + DUP1 %insert_accessed_addresses_no_return // stack: from, to, amount, retdest %transfer_eth // stack: transfer_eth_status, retdest %jumpi(process_message_txn_insufficient_balance) // stack: retdest + // Add precompiles to accessed addresses. + PUSH @ECREC %insert_accessed_addresses_no_return + PUSH @SHA256 %insert_accessed_addresses_no_return + PUSH @RIP160 %insert_accessed_addresses_no_return + PUSH @ID %insert_accessed_addresses_no_return + PUSH @EXPMOD %insert_accessed_addresses_no_return + PUSH @BN_ADD %insert_accessed_addresses_no_return + PUSH @BN_MUL %insert_accessed_addresses_no_return + PUSH @SNARKV %insert_accessed_addresses_no_return + PUSH @BLAKE2_F %insert_accessed_addresses_no_return // TODO: Handle precompiles. // If to's code is empty, return. diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 26fb113c..c124db34 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -19,8 +19,13 @@ global sys_return: %jump(terminate_common) global sys_selfdestruct: - // stack: kexit_info + // stack: kexit_info, address + SWAP1 %u256_to_addr + DUP1 %insert_accessed_addresses_no_return // TODO: Use return value in gas calculation. + // stack: address, kexit_info + SWAP1 // TODO: Charge gas. + // TODO: Add address to the access list. %consume_gas_const(@GAS_SELFDESTRUCT) %leftover_gas // stack: leftover_gas diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm index cf7ed1e9..edb63e2d 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm @@ -5,9 +5,19 @@ global sys_sload: // stack: kexit_info, slot - // TODO: Charge gas. SWAP1 // stack: slot, kexit_info + DUP1 %address + // stack: addr, slot, slot, kexit_info + %insert_accessed_storage_keys PUSH @GAS_COLDSLOAD_MINUS_WARMACCESS + MUL + PUSH @GAS_WARMACCESS + ADD + %stack (gas, slot, kexit_info) -> (gas, kexit_info, slot) + %charge_gas + // stack: kexit_info, slot + + SWAP1 %stack (slot) -> (slot, after_storage_read) %slot_to_storage_key // stack: storage_key, after_storage_read, kexit_info diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm index f56a5fdf..90fb0e0b 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -4,6 +4,8 @@ // Post stack: (empty) global sys_sstore: + %stack (kexit_info, slot, value) -> (slot, kexit_info, slot, value) + %address %insert_accessed_storage_keys POP // TODO: Use return value in gas calculation. // TODO: Assuming a cold zero -> nonzero write for now. PUSH @GAS_COLDSLOAD PUSH @GAS_SSET diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index 476f1c74..2ba6ad0b 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -345,3 +345,8 @@ %endrep // stack: a || b || c || d %endmacro + +%macro u256_to_addr + // stack: x + %mod_const(0x10000000000000000000000000000000000000000) // 2^160 +%endmacro diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index e9d694eb..f04c06e1 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -45,10 +45,14 @@ pub(crate) enum GlobalMetadata { /// Gas to refund at the end of the transaction. RefundCounter = 22, + /// Length of the addresses access list. + AccessedAddressesLen = 23, + /// Length of the storage keys access list. + AccessedStorageKeysLen = 24, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 22; + pub(crate) const COUNT: usize = 24; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -74,6 +78,8 @@ impl GlobalMetadata { Self::BlockChainId, Self::BlockBaseFee, Self::RefundCounter, + Self::AccessedAddressesLen, + Self::AccessedStorageKeysLen, ] } @@ -102,6 +108,8 @@ impl GlobalMetadata { Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID", Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE", Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", + Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN", + Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN", } } } diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index d0a079b8..2889e7c7 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -32,6 +32,10 @@ pub fn evm_constants() -> HashMap { c.insert(name.into(), U256::from(value)); } + for (name, value) in PRECOMPILES { + c.insert(name.into(), U256::from(value)); + } + for segment in Segment::all() { c.insert(segment.var_name().into(), (segment as u32).into()); } @@ -151,7 +155,7 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 18] = [ ), ]; -const GAS_CONSTANTS: [(&str, u16); 36] = [ +const GAS_CONSTANTS: [(&str, u16); 38] = [ ("GAS_ZERO", 0), ("GAS_JUMPDEST", 1), ("GAS_BASE", 2), @@ -163,7 +167,9 @@ const GAS_CONSTANTS: [(&str, u16); 36] = [ ("GAS_ACCESSLISTADDRESS", 2_400), ("GAS_ACCESSLISTSTORAGE", 1_900), ("GAS_COLDACCOUNTACCESS", 2_600), + ("GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS", 2_500), ("GAS_COLDSLOAD", 2_100), + ("GAS_COLDSLOAD_MINUS_WARMACCESS", 2_000), ("GAS_SSET", 20_000), ("GAS_SRESET", 2_900), ("REFUND_SCLEAR", 15_000), @@ -189,3 +195,15 @@ const GAS_CONSTANTS: [(&str, u16); 36] = [ ("GAS_COPY", 3), ("GAS_BLOCKHASH", 20), ]; + +const PRECOMPILES: [(&str, u16); 9] = [ + ("ECREC", 1), + ("SHA256", 2), + ("RIP160", 3), + ("ID", 4), + ("EXPMOD", 5), + ("BN_ADD", 6), + ("BN_MUL", 7), + ("SNARKV", 8), + ("BLAKE2_F", 9), +]; diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 408671ba..8347d896 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -59,6 +59,7 @@ pub fn run_interpreter( ) } +#[derive(Clone)] pub struct InterpreterMemoryInitialization { pub label: String, pub stack: Vec, diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs new file mode 100644 index 00000000..2b647811 --- /dev/null +++ b/evm/src/cpu/kernel/tests/core/access_lists.rs @@ -0,0 +1,195 @@ +use std::collections::HashSet; + +use anyhow::Result; +use ethereum_types::{Address, U256}; +use rand::{thread_rng, Rng}; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata::{ + AccessedAddressesLen, AccessedStorageKeysLen, +}; +use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys, GlobalMetadata}; +use crate::witness::memory::MemoryAddress; + +#[test] +fn test_insert_accessed_addresses() -> Result<()> { + let insert_accessed_addresses = KERNEL.global_labels["insert_accessed_addresses"]; + + let retaddr = 0xdeadbeefu32.into(); + let mut rng = thread_rng(); + let n = rng.gen_range(1..10); + let addresses = (0..n) + .map(|_| rng.gen::
()) + .collect::>() + .into_iter() + .collect::>(); + let addr_in_list = addresses[rng.gen_range(0..n)]; + let addr_not_in_list = rng.gen::
(); + assert!( + !addresses.contains(&addr_not_in_list), + "Cosmic luck or bad RNG?" + ); + + // Test for address already in list. + let initial_stack = vec![retaddr, U256::from(addr_in_list.0.as_slice())]; + let mut interpreter = Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); + for i in 0..n { + let addr = U256::from(addresses[i].0.as_slice()); + interpreter + .generation_state + .memory + .set(MemoryAddress::new(0, AccessedAddresses, i), addr); + } + interpreter.generation_state.memory.set( + MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + U256::from(n), + ); + interpreter.run()?; + assert_eq!(interpreter.stack(), &[U256::zero()]); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + GlobalMetadata, + AccessedAddressesLen as usize + )), + U256::from(n) + ); + + // Test for address not in list. + let initial_stack = vec![retaddr, U256::from(addr_not_in_list.0.as_slice())]; + let mut interpreter = Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); + for i in 0..n { + let addr = U256::from(addresses[i].0.as_slice()); + interpreter + .generation_state + .memory + .set(MemoryAddress::new(0, AccessedAddresses, i), addr); + } + interpreter.generation_state.memory.set( + MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + U256::from(n), + ); + interpreter.run()?; + assert_eq!(interpreter.stack(), &[U256::one()]); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + GlobalMetadata, + AccessedAddressesLen as usize + )), + U256::from(n + 1) + ); + assert_eq!( + interpreter + .generation_state + .memory + .get(MemoryAddress::new(0, AccessedAddresses, n)), + U256::from(addr_not_in_list.0.as_slice()) + ); + + Ok(()) +} + +#[test] +fn test_insert_accessed_storage_keys() -> Result<()> { + let insert_accessed_storage_keys = KERNEL.global_labels["insert_accessed_storage_keys"]; + + let retaddr = 0xdeadbeefu32.into(); + let mut rng = thread_rng(); + let n = rng.gen_range(1..10); + let storage_keys = (0..n) + .map(|_| (rng.gen::
(), U256(rng.gen()))) + .collect::>() + .into_iter() + .collect::>(); + let storage_key_in_list = storage_keys[rng.gen_range(0..n)]; + let storage_key_not_in_list = (rng.gen::
(), U256(rng.gen())); + assert!( + !storage_keys.contains(&storage_key_not_in_list), + "Cosmic luck or bad RNG?" + ); + + // Test for storage key already in list. + let initial_stack = vec![ + retaddr, + storage_key_in_list.1, + U256::from(storage_key_in_list.0 .0.as_slice()), + ]; + let mut interpreter = Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); + for i in 0..n { + let addr = U256::from(storage_keys[i].0 .0.as_slice()); + interpreter + .generation_state + .memory + .set(MemoryAddress::new(0, AccessedStorageKeys, 2 * i), addr); + interpreter.generation_state.memory.set( + MemoryAddress::new(0, AccessedStorageKeys, 2 * i + 1), + storage_keys[i].1, + ); + } + interpreter.generation_state.memory.set( + MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + U256::from(2 * n), + ); + interpreter.run()?; + assert_eq!(interpreter.stack(), &[U256::zero()]); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + GlobalMetadata, + AccessedStorageKeysLen as usize + )), + U256::from(2 * n) + ); + + // Test for storage key not in list. + let initial_stack = vec![ + retaddr, + storage_key_not_in_list.1, + U256::from(storage_key_not_in_list.0 .0.as_slice()), + ]; + let mut interpreter = Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); + for i in 0..n { + let addr = U256::from(storage_keys[i].0 .0.as_slice()); + interpreter + .generation_state + .memory + .set(MemoryAddress::new(0, AccessedStorageKeys, 2 * i), addr); + interpreter.generation_state.memory.set( + MemoryAddress::new(0, AccessedStorageKeys, 2 * i + 1), + storage_keys[i].1, + ); + } + interpreter.generation_state.memory.set( + MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + U256::from(2 * n), + ); + interpreter.run()?; + assert_eq!(interpreter.stack(), &[U256::one()]); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + GlobalMetadata, + AccessedStorageKeysLen as usize + )), + U256::from(2 * (n + 1)) + ); + assert_eq!( + interpreter + .generation_state + .memory + .get(MemoryAddress::new(0, AccessedStorageKeys, 2 * n,)), + U256::from(storage_key_not_in_list.0 .0.as_slice()) + ); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + AccessedStorageKeys, + 2 * n + 1, + )), + storage_key_not_in_list.1 + ); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/core/mod.rs b/evm/src/cpu/kernel/tests/core/mod.rs index 502c57f1..8d71051c 100644 --- a/evm/src/cpu/kernel/tests/core/mod.rs +++ b/evm/src/cpu/kernel/tests/core/mod.rs @@ -1,3 +1,4 @@ +mod access_lists; mod create_addresses; mod intrinsic_gas; mod jumpdest_analysis; diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index ee76cf7a..61c5a911 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -44,10 +44,14 @@ pub enum Segment { BnWnafB = 20, BnTableQ = 21, BnPairing = 22, + /// List of addresses that have been accessed in the current transaction. + AccessedAddresses = 23, + /// List of storage keys that have been accessed in the current transaction. + AccessedStorageKeys = 24, } impl Segment { - pub(crate) const COUNT: usize = 23; + pub(crate) const COUNT: usize = 25; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -74,6 +78,8 @@ impl Segment { Self::BnWnafB, Self::BnTableQ, Self::BnPairing, + Self::AccessedAddresses, + Self::AccessedStorageKeys, ] } @@ -103,6 +109,8 @@ impl Segment { Segment::BnWnafB => "SEGMENT_KERNEL_BN_WNAF_B", Segment::BnTableQ => "SEGMENT_KERNEL_BN_TABLE_Q", Segment::BnPairing => "SEGMENT_KERNEL_BN_PAIRING", + Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES", + Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS", } } @@ -132,6 +140,8 @@ impl Segment { Segment::BnWnafB => 8, Segment::BnTableQ => 256, Segment::BnPairing => 256, + Segment::AccessedAddresses => 256, + Segment::AccessedStorageKeys => 256, } } } From cf5a4edc8b73f5e311b5985a71fa6a4165dae869 Mon Sep 17 00:00:00 2001 From: Dmitry Vagner Date: Mon, 27 Mar 2023 18:20:22 -0700 Subject: [PATCH 39/39] prover input minor improvements --- evm/src/generation/prover_input.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 74321562..ea834164 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -61,13 +61,9 @@ impl GenerationState { fn run_sf(&self, input_fn: &ProverInputFn) -> U256 { let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap(); let inputs: [U256; 4] = match field { - Bls381Base => { - let mut inputs: [U256; 4] = [U256::zero(); 4]; - for i in 0..4 { - inputs[i] = stack_peek(self, i).expect("Empty stack"); - } - inputs - } + Bls381Base => std::array::from_fn(|i| { + stack_peek(self, i).expect("Insufficient number of items on stack") + }), _ => todo!(), }; match input_fn.0[2].as_str() { @@ -91,16 +87,12 @@ impl GenerationState { .unwrap() .parse::() .unwrap(); - let ptr = stack_peek(self, 11 - n).expect("Empty stack").as_usize(); + let ptr = stack_peek(self, 11 - n) + .expect("Insufficient number of items on stack") + .as_usize(); let f: [U256; 12] = match field { - Bn254Base => { - let mut f: [U256; 12] = [U256::zero(); 12]; - for i in 0..12 { - f[i] = kernel_peek(self, BnPairing, ptr + i); - } - f - } + Bn254Base => std::array::from_fn(|i| kernel_peek(self, BnPairing, ptr + i)), _ => todo!(), }; field.field_extension_inverse(n, f)