mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-11 10:13:09 +00:00
Merge branch 'main' into bignum-modexp
This commit is contained in:
commit
c18377d12f
@ -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;
|
||||
|
||||
@ -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<Fp> for Standard {
|
||||
fn sample<R: Rng + ?Sized>(&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<Fp2> for Standard {
|
||||
fn sample<R: Rng + ?Sized>(&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<Fp6> for Standard {
|
||||
fn sample<R: Rng + ?Sized>(&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<U256> {
|
||||
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<Fp12> for Standard {
|
||||
fn sample<R: Rng + ?Sized>(&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<U256> {
|
||||
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,
|
||||
]),
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -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<BN254>,
|
||||
pub y: Fp2<BN254>,
|
||||
}
|
||||
|
||||
// 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<BN254> {
|
||||
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<BN254> {
|
||||
let mut r = p;
|
||||
let mut acc = UNIT_FP12;
|
||||
let mut line;
|
||||
let mut acc: Fp12<BN254> = Fp12::<BN254>::UNIT;
|
||||
let mut line: Fp12<BN254>;
|
||||
|
||||
for i in EXP {
|
||||
line = tangent(r, q);
|
||||
@ -66,42 +66,46 @@ pub fn miller_loop(p: Curve, q: TwistedCurve) -> Fp12 {
|
||||
}
|
||||
|
||||
/// The sloped line function for doubling a point
|
||||
pub fn tangent(p: Curve, q: TwistedCurve) -> Fp12 {
|
||||
let cx = -Fp::new(3) * p.x * p.x;
|
||||
let cy = Fp::new(2) * p.y;
|
||||
sparse_embed(p.y * p.y - Fp::new(9), q.x.scale(cx), q.y.scale(cy))
|
||||
pub fn tangent(p: Curve, q: TwistedCurve) -> Fp12<BN254> {
|
||||
let cx = -BN254::new(3) * p.x * p.x;
|
||||
let cy = BN254::new(2) * p.y;
|
||||
sparse_embed(p.y * p.y - BN254::new(9), q.x * cx, q.y * cy)
|
||||
}
|
||||
|
||||
/// The sloped line function for adding two points
|
||||
pub fn cord(p1: Curve, p2: Curve, q: TwistedCurve) -> Fp12 {
|
||||
pub fn cord(p1: Curve, p2: Curve, q: TwistedCurve) -> Fp12<BN254> {
|
||||
let cx = p2.y - p1.y;
|
||||
let cy = p1.x - p2.x;
|
||||
sparse_embed(p1.y * p2.x - p2.y * p1.x, q.x.scale(cx), q.y.scale(cy))
|
||||
sparse_embed(p1.y * p2.x - p2.y * p1.x, q.x * cx, q.y * cy)
|
||||
}
|
||||
|
||||
/// The tangent and cord functions output sparse Fp12 elements.
|
||||
/// This map embeds the nonzero coefficients into an Fp12.
|
||||
pub fn sparse_embed(g000: Fp, g01: Fp2, g11: Fp2) -> Fp12 {
|
||||
pub fn sparse_embed(g000: BN254, g01: Fp2<BN254>, g11: Fp2<BN254>) -> Fp12<BN254> {
|
||||
let g0 = Fp6 {
|
||||
t0: Fp2 {
|
||||
re: g000,
|
||||
im: ZERO_FP,
|
||||
im: BN254::ZERO,
|
||||
},
|
||||
t1: g01,
|
||||
t2: ZERO_FP2,
|
||||
t2: Fp2::<BN254>::ZERO,
|
||||
};
|
||||
|
||||
let g1 = Fp6 {
|
||||
t0: ZERO_FP2,
|
||||
t0: Fp2::<BN254>::ZERO,
|
||||
t1: g11,
|
||||
t2: ZERO_FP2,
|
||||
t2: Fp2::<BN254>::ZERO,
|
||||
};
|
||||
|
||||
Fp12 { z0: g0, z1: g1 }
|
||||
}
|
||||
|
||||
pub fn gen_fp12_sparse<R: Rng + ?Sized>(rng: &mut R) -> Fp12 {
|
||||
sparse_embed(rng.gen::<Fp>(), rng.gen::<Fp2>(), rng.gen::<Fp2>())
|
||||
pub fn gen_fp12_sparse<R: Rng + ?Sized>(rng: &mut R) -> Fp12<BN254> {
|
||||
sparse_embed(
|
||||
rng.gen::<BN254>(),
|
||||
rng.gen::<Fp2<BN254>>(),
|
||||
rng.gen::<Fp2<BN254>>(),
|
||||
)
|
||||
}
|
||||
|
||||
/// The output y of the miller loop is not an invariant,
|
||||
@ -116,7 +120,7 @@ pub fn gen_fp12_sparse<R: Rng + ?Sized>(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<BN254>) -> Fp12<BN254> {
|
||||
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<BN254>) -> (Fp12<BN254>, Fp12<BN254>, Fp12<BN254>) {
|
||||
let mut sq: Fp12<BN254> = f;
|
||||
let mut y0: Fp12<BN254> = Fp12::<BN254>::UNIT;
|
||||
let mut y2: Fp12<BN254> = Fp12::<BN254>::UNIT;
|
||||
let mut y4: Fp12<BN254> = Fp12::<BN254>::UNIT;
|
||||
|
||||
// proceed via standard squaring algorithm for exponentiation
|
||||
|
||||
|
||||
@ -37,6 +37,8 @@ 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/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"),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
102
evm/src/cpu/kernel/asm/core/access_lists.asm
Normal file
102
evm/src/cpu/kernel/asm/core/access_lists.asm
Normal file
@ -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
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -127,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.
|
||||
|
||||
@ -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
|
||||
|
||||
53
evm/src/cpu/kernel/asm/curve/bls381/util.asm
Normal file
53
evm/src/cpu/kernel/asm/curve/bls381/util.asm
Normal file
@ -0,0 +1,53 @@
|
||||
%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
|
||||
|
||||
global test_add_fp381:
|
||||
%add_fp381
|
||||
%jump(0xdeadbeef)
|
||||
|
||||
global test_mul_fp381:
|
||||
%mul_fp381
|
||||
%jump(0xdeadbeef)
|
||||
|
||||
global test_sub_fp381:
|
||||
%sub_fp381
|
||||
%jump(0xdeadbeef)
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -357,3 +357,8 @@
|
||||
%endrep
|
||||
// stack: a || b || c || d
|
||||
%endmacro
|
||||
|
||||
%macro u256_to_addr
|
||||
// stack: x
|
||||
%mod_const(0x10000000000000000000000000000000000000000) // 2^160
|
||||
%endmacro
|
||||
|
||||
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,10 @@ pub fn evm_constants() -> HashMap<String, U256> {
|
||||
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),
|
||||
];
|
||||
|
||||
@ -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;
|
||||
@ -59,6 +59,7 @@ pub fn run_interpreter(
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InterpreterMemoryInitialization {
|
||||
pub label: String,
|
||||
pub stack: Vec<U256>,
|
||||
|
||||
42
evm/src/cpu/kernel/tests/bls381.rs
Normal file
42
evm/src/cpu/kernel/tests/bls381.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use anyhow::Result;
|
||||
use ethereum_types::U512;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::cpu::kernel::interpreter::{
|
||||
run_interpreter_with_memory, InterpreterMemoryInitialization,
|
||||
};
|
||||
use crate::extension_tower::{Stack, BLS381};
|
||||
use crate::memory::segments::Segment::KernelGeneral;
|
||||
|
||||
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]
|
||||
fn test_bls_ops() -> Result<()> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let x: BLS381 = rng.gen::<BLS381>();
|
||||
let y: BLS381 = rng.gen::<BLS381>();
|
||||
|
||||
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);
|
||||
|
||||
assert_eq!(output_add, x + y);
|
||||
assert_eq!(output_mul, x * y);
|
||||
assert_eq!(output_sub, x - y);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -2,13 +2,13 @@ use anyhow::Result;
|
||||
use ethereum_types::U256;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::bn254_arithmetic::{Fp, Fp12, Fp2, Fp6};
|
||||
use crate::bn254_pairing::{
|
||||
gen_fp12_sparse, invariant_exponent, miller_loop, tate, Curve, TwistedCurve,
|
||||
};
|
||||
use crate::cpu::kernel::interpreter::{
|
||||
run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization,
|
||||
};
|
||||
use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254};
|
||||
use crate::memory::segments::Segment::BnPairing;
|
||||
|
||||
fn extract_stack(interpreter: Interpreter<'static>) -> Vec<U256> {
|
||||
@ -20,7 +20,11 @@ fn extract_stack(interpreter: Interpreter<'static>) -> Vec<U256> {
|
||||
.collect::<Vec<U256>>()
|
||||
}
|
||||
|
||||
fn setup_mul_fp6_test(f: Fp6, g: Fp6, label: &str) -> InterpreterMemoryInitialization {
|
||||
fn setup_mul_fp6_test(
|
||||
f: Fp6<BN254>,
|
||||
g: Fp6<BN254>,
|
||||
label: &str,
|
||||
) -> InterpreterMemoryInitialization {
|
||||
let mut stack = f.on_stack();
|
||||
if label == "mul_fp254_6" {
|
||||
stack.extend(g.on_stack());
|
||||
@ -37,8 +41,8 @@ fn setup_mul_fp6_test(f: Fp6, g: Fp6, label: &str) -> InterpreterMemoryInitializ
|
||||
#[test]
|
||||
fn test_mul_fp6() -> Result<()> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp6 = rng.gen::<Fp6>();
|
||||
let g: Fp6 = rng.gen::<Fp6>();
|
||||
let f: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
|
||||
let g: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
|
||||
|
||||
let setup_normal: InterpreterMemoryInitialization = setup_mul_fp6_test(f, g, "mul_fp254_6");
|
||||
let setup_square: InterpreterMemoryInitialization = setup_mul_fp6_test(f, f, "square_fp254_6");
|
||||
@ -60,8 +64,8 @@ fn test_mul_fp6() -> Result<()> {
|
||||
|
||||
fn setup_mul_fp12_test(
|
||||
out: usize,
|
||||
f: Fp12,
|
||||
g: Fp12,
|
||||
f: Fp12<BN254>,
|
||||
g: Fp12<BN254>,
|
||||
label: &str,
|
||||
) -> InterpreterMemoryInitialization {
|
||||
let in0: usize = 200;
|
||||
@ -89,9 +93,9 @@ fn test_mul_fp12() -> Result<()> {
|
||||
let out: usize = 224;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp12 = rng.gen::<Fp12>();
|
||||
let g: Fp12 = rng.gen::<Fp12>();
|
||||
let h: Fp12 = gen_fp12_sparse(&mut rng);
|
||||
let f: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
|
||||
let g: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
|
||||
let h: Fp12<BN254> = gen_fp12_sparse(&mut rng);
|
||||
|
||||
let setup_normal: InterpreterMemoryInitialization =
|
||||
setup_mul_fp12_test(out, f, g, "mul_fp254_12");
|
||||
@ -119,7 +123,7 @@ fn test_mul_fp12() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_frob_fp6_test(f: Fp6, n: usize) -> InterpreterMemoryInitialization {
|
||||
fn setup_frob_fp6_test(f: Fp6<BN254>, n: usize) -> InterpreterMemoryInitialization {
|
||||
InterpreterMemoryInitialization {
|
||||
label: String::from("test_frob_fp254_6_") + &(n.to_string()),
|
||||
stack: f.on_stack(),
|
||||
@ -131,7 +135,7 @@ fn setup_frob_fp6_test(f: Fp6, n: usize) -> InterpreterMemoryInitialization {
|
||||
#[test]
|
||||
fn test_frob_fp6() -> Result<()> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp6 = rng.gen::<Fp6>();
|
||||
let f: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
|
||||
for n in 1..4 {
|
||||
let setup_frob = setup_frob_fp6_test(f, n);
|
||||
let intrptr_frob: Interpreter = run_interpreter_with_memory(setup_frob).unwrap();
|
||||
@ -142,7 +146,7 @@ fn test_frob_fp6() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryInitialization {
|
||||
fn setup_frob_fp12_test(ptr: usize, f: Fp12<BN254>, n: usize) -> InterpreterMemoryInitialization {
|
||||
InterpreterMemoryInitialization {
|
||||
label: String::from("test_frob_fp254_12_") + &(n.to_string()),
|
||||
stack: vec![U256::from(ptr)],
|
||||
@ -155,7 +159,7 @@ fn setup_frob_fp12_test(ptr: usize, f: Fp12, n: usize) -> InterpreterMemoryIniti
|
||||
fn test_frob_fp12() -> Result<()> {
|
||||
let ptr: usize = 200;
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp12 = rng.gen::<Fp12>();
|
||||
let f: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
|
||||
for n in [1, 2, 3, 6] {
|
||||
let setup_frob = setup_frob_fp12_test(ptr, f, n);
|
||||
let intrptr_frob: Interpreter = run_interpreter_with_memory(setup_frob).unwrap();
|
||||
@ -171,7 +175,7 @@ fn test_inv_fp12() -> Result<()> {
|
||||
let ptr: usize = 200;
|
||||
let inv: usize = 212;
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp12 = rng.gen::<Fp12>();
|
||||
let f: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
|
||||
|
||||
let setup = InterpreterMemoryInitialization {
|
||||
label: "inv_fp254_12".to_string(),
|
||||
@ -192,7 +196,7 @@ fn test_inv_fp12() -> Result<()> {
|
||||
fn test_invariant_exponent() -> Result<()> {
|
||||
let ptr: usize = 200;
|
||||
let mut rng = rand::thread_rng();
|
||||
let f: Fp12 = rng.gen::<Fp12>();
|
||||
let f: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
|
||||
|
||||
let setup = InterpreterMemoryInitialization {
|
||||
label: "bn254_invariant_exponent".to_string(),
|
||||
@ -213,8 +217,8 @@ fn test_invariant_exponent() -> Result<()> {
|
||||
// The curve is cyclic with generator (1, 2)
|
||||
pub const CURVE_GENERATOR: Curve = {
|
||||
Curve {
|
||||
x: Fp { val: U256::one() },
|
||||
y: Fp {
|
||||
x: BN254 { val: U256::one() },
|
||||
y: BN254 {
|
||||
val: U256([2, 0, 0, 0]),
|
||||
},
|
||||
}
|
||||
@ -224,7 +228,7 @@ pub const CURVE_GENERATOR: Curve = {
|
||||
pub const TWISTED_GENERATOR: TwistedCurve = {
|
||||
TwistedCurve {
|
||||
x: Fp2 {
|
||||
re: Fp {
|
||||
re: BN254 {
|
||||
val: U256([
|
||||
0x46debd5cd992f6ed,
|
||||
0x674322d4f75edadd,
|
||||
@ -232,7 +236,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = {
|
||||
0x1800deef121f1e76,
|
||||
]),
|
||||
},
|
||||
im: Fp {
|
||||
im: BN254 {
|
||||
val: U256([
|
||||
0x97e485b7aef312c2,
|
||||
0xf1aa493335a9e712,
|
||||
@ -242,7 +246,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = {
|
||||
},
|
||||
},
|
||||
y: Fp2 {
|
||||
re: Fp {
|
||||
re: BN254 {
|
||||
val: U256([
|
||||
0x4ce6cc0166fa7daa,
|
||||
0xe3d1e7690c43d37b,
|
||||
@ -250,7 +254,7 @@ pub const TWISTED_GENERATOR: TwistedCurve = {
|
||||
0x12c85ea5db8c6deb,
|
||||
]),
|
||||
},
|
||||
im: Fp {
|
||||
im: BN254 {
|
||||
val: U256([
|
||||
0x55acdadcd122975b,
|
||||
0xbc4b313370b38ef3,
|
||||
|
||||
195
evm/src/cpu/kernel/tests/core/access_lists.rs
Normal file
195
evm/src/cpu/kernel/tests/core/access_lists.rs
Normal file
@ -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::<Address>())
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<Address>>();
|
||||
let addr_in_list = addresses[rng.gen_range(0..n)];
|
||||
let addr_not_in_list = rng.gen::<Address>();
|
||||
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::<Address>(), U256(rng.gen())))
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<(Address, U256)>>();
|
||||
let storage_key_in_list = storage_keys[rng.gen_range(0..n)];
|
||||
let storage_key_not_in_list = (rng.gen::<Address>(), 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(())
|
||||
}
|
||||
@ -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<Create2TestCase> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
mod access_lists;
|
||||
mod create_addresses;
|
||||
mod intrinsic_gas;
|
||||
mod jumpdest_analysis;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
mod account_code;
|
||||
mod balance;
|
||||
mod bignum;
|
||||
mod bls381;
|
||||
mod bn254;
|
||||
mod core;
|
||||
mod ecc;
|
||||
|
||||
1225
evm/src/extension_tower.rs
Normal file
1225
evm/src/extension_tower.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,13 +2,13 @@ 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 itertools::Itertools;
|
||||
use plonky2::field::types::Field;
|
||||
|
||||
use crate::bn254_arithmetic::Fp12;
|
||||
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;
|
||||
@ -33,6 +33,7 @@ impl<F: Field> GenerationState<F> {
|
||||
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(),
|
||||
@ -60,6 +61,26 @@ impl<F: Field> GenerationState<F> {
|
||||
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 inputs: [U256; 4] = match field {
|
||||
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() {
|
||||
"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.
|
||||
fn run_ffe(&self, input_fn: &ProverInputFn) -> U256 {
|
||||
let field = EvmField::from_str(input_fn.0[1].as_str()).unwrap();
|
||||
@ -70,16 +91,12 @@ impl<F: Field> GenerationState<F> {
|
||||
.unwrap()
|
||||
.parse::<usize>()
|
||||
.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)
|
||||
@ -193,6 +210,8 @@ impl<F: Field> GenerationState<F> {
|
||||
}
|
||||
|
||||
enum EvmField {
|
||||
Bls381Base,
|
||||
Bls381Scalar,
|
||||
Bn254Base,
|
||||
Bn254Scalar,
|
||||
Secp256k1Base,
|
||||
@ -209,6 +228,8 @@ impl FromStr for EvmField {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"bls381_base" => Bls381Base,
|
||||
"bls381_scalar" => Bls381Scalar,
|
||||
"bn254_base" => Bn254Base,
|
||||
"bn254_scalar" => Bn254Scalar,
|
||||
"secp256k1_base" => Secp256k1Base,
|
||||
@ -233,6 +254,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()
|
||||
@ -273,8 +296,56 @@ 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: Fp12<BN254> = unsafe { transmute(f) };
|
||||
let f_inv: [U256; 12] = unsafe { transmute(f.inv()) };
|
||||
f_inv[n]
|
||||
}
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
|
||||
pub mod all_stark;
|
||||
pub mod arithmetic;
|
||||
pub mod bn254_arithmetic;
|
||||
pub mod bn254_pairing;
|
||||
pub mod config;
|
||||
pub mod constraint_consumer;
|
||||
pub mod cpu;
|
||||
pub mod cross_table_lookup;
|
||||
pub mod extension_tower;
|
||||
pub mod fixed_recursive_verifier;
|
||||
pub mod generation;
|
||||
mod get_challenges;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<F: Field>(
|
||||
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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user