Merge pull request #906 from mir-protocol/fp318

Generalize rust code for field extension arithmetic to be base-field agnostic
This commit is contained in:
Dima V 2023-03-22 17:40:58 -07:00 committed by GitHub
commit 1ce47cebc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1275 additions and 932 deletions

View File

@ -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;

View File

@ -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,
]),
}
},
},
];

View File

@ -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

View File

@ -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;

View File

@ -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,

1211
evm/src/extension_tower.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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<BN254> = unsafe { transmute(f) };
let f_inv: [U256; 12] = unsafe { transmute(f.inv()) };
f_inv[n]
}

View File

@ -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;