Merge pull request #941 from mir-protocol/bls-fp2

BLS Fp2 Operations and More Systematic Test API
This commit is contained in:
Dima V 2023-05-16 15:22:04 +02:00 committed by GitHub
commit c36ed15e2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 206 additions and 130 deletions

View File

@ -12,20 +12,6 @@
// 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)
@ -40,14 +26,76 @@
// stack: z0, z1
%endmacro
global test_add_fp381:
%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 add_fp381_2
// stack: x_re, x_im, y_re, y_im
%stack (x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (y_im, x_im, y_re, x_re)
// stack: y_im, x_im, y_re, x_re
%add_fp381
%jump(0xdeadbeef)
// stack: z_im, y_re, x_re
%stack (z_im: 2, y_re: 2, x_re: 2) -> (x_re, y_re, z_im)
// stack: x_re, y_re, z_im
%add_fp381
// stack: z_re, z_im
%endmacro
global test_mul_fp381:
%mul_fp381
%jump(0xdeadbeef)
global test_sub_fp381:
%macro sub_fp381_2
// stack: x_re, x_im, y_re, y_im
%stack (x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (x_im, y_im, y_re, x_re)
// stack: x_im, y_im, y_re, x_re
%sub_fp381
%jump(0xdeadbeef)
// stack: z_im, y_re, x_re
%stack (z_im: 2, y_re: 2, x_re: 2) -> (x_re, y_re, z_im)
// stack: x_re, y_re, z_im
%sub_fp381
// stack: z_re, z_im
%endmacro
// note that {x,y}_{re,im} all take up two stack terms
global mul_fp381_2:
// stack: x_re, x_im, y_re, y_im, jumpdest
DUP4
DUP4
// stack: x_im, x_re, x_im, y_re, y_im, jumpdest
DUP8
DUP8
// stack: y_re, x_im, x_re, x_im, y_re, y_im, jumpdest
DUP12
DUP12
// stack: y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest
DUP8
DUP8
// stack: x_re , y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest
%mul_fp381
// stack: x_re * y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest
%stack (v: 2, y_re: 2, x_im: 2) -> (x_im, y_re, v)
// stack: x_im , y_re, x_re*y_im, x_re, x_im, y_re, y_im, jumpdest
%mul_fp381
// stack: x_im * y_re, x_re*y_im, x_re, x_im, y_re, y_im, jumpdest
%add_fp381
// stack: z_im, x_re, x_im, y_re, y_im, jumpdest
%stack (z_im: 2, x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (x_im, y_im, y_re, x_re, z_im)
// stack: x_im , y_im, y_re, x_re, z_im, jumpdest
%mul_fp381
// stack: x_im * y_im, y_re, x_re, z_im, jumpdest
%stack (v: 2, y_re: 2, x_re: 2) -> (x_re, y_re, v)
// stack: x_re , y_re, x_im*y_im, z_im, jumpdest
%mul_fp381
// stack: x_re * y_re, x_im*y_im, z_im, jumpdest
%sub_fp381
// stack: z_re, z_im, jumpdest
%stack (z_re: 2, z_im: 2, jumpdest) -> (jumpdest, z_re, z_im)
JUMP

View File

@ -1,42 +1,32 @@
use anyhow::Result;
use ethereum_types::U512;
use ethereum_types::U256;
use rand::Rng;
use crate::cpu::kernel::interpreter::{
run_interpreter_with_memory, InterpreterMemoryInitialization,
};
use crate::extension_tower::{Stack, BLS381};
use crate::extension_tower::{Fp2, 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());
#[test]
fn test_bls_fp2_mul() -> Result<()> {
let mut rng = rand::thread_rng();
let x: Fp2<BLS381> = rng.gen::<Fp2<BLS381>>();
let y: Fp2<BLS381> = rng.gen::<Fp2<BLS381>>();
let mut stack = x.to_stack().to_vec();
stack.extend(y.to_stack().to_vec());
stack.push(U256::from(0xdeadbeefu32));
let setup = InterpreterMemoryInitialization {
label,
label: "mul_fp381_2".to_string(),
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);
let stack: Vec<U256> = interpreter.stack().iter().rev().cloned().collect();
let output = Fp2::<BLS381>::from_stack(&stack);
assert_eq!(output, x * y);
Ok(())
}

View File

@ -11,22 +11,12 @@ use crate::curve_pairings::{
use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254};
use crate::memory::segments::Segment::BnPairing;
fn extract_stack(interpreter: Interpreter<'static>) -> Vec<U256> {
interpreter
.stack()
.iter()
.rev()
.cloned()
.collect::<Vec<U256>>()
}
fn run_bn_mul_fp6(f: Fp6<BN254>, g: Fp6<BN254>, label: &str) -> Vec<U256> {
let mut stack = f.on_stack();
fn run_bn_mul_fp6(f: Fp6<BN254>, g: Fp6<BN254>, label: &str) -> Fp6<BN254> {
let mut stack = f.to_stack();
if label == "mul_fp254_6" {
stack.extend(g.on_stack());
stack.extend(g.to_stack().to_vec());
}
stack.push(U256::from(0xdeadbeefu32));
let setup = InterpreterMemoryInitialization {
label: label.to_string(),
stack,
@ -34,7 +24,8 @@ fn run_bn_mul_fp6(f: Fp6<BN254>, g: Fp6<BN254>, label: &str) -> Vec<U256> {
memory: vec![],
};
let interpreter = run_interpreter_with_memory(setup).unwrap();
extract_stack(interpreter)
let output: Vec<U256> = interpreter.stack().iter().rev().cloned().collect();
Fp6::<BN254>::from_stack(&output)
}
#[test]
@ -43,19 +34,16 @@ fn test_bn_mul_fp6() -> Result<()> {
let f: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
let g: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
let out_normal: Vec<U256> = run_bn_mul_fp6(f, g, "mul_fp254_6");
let out_square: Vec<U256> = run_bn_mul_fp6(f, f, "square_fp254_6");
let output_normal: Fp6<BN254> = run_bn_mul_fp6(f, g, "mul_fp254_6");
let output_square: Fp6<BN254> = run_bn_mul_fp6(f, f, "square_fp254_6");
let exp_normal: Vec<U256> = (f * g).on_stack();
let exp_square: Vec<U256> = (f * f).on_stack();
assert_eq!(out_normal, exp_normal);
assert_eq!(out_square, exp_square);
assert_eq!(output_normal, f * g);
assert_eq!(output_square, f * f);
Ok(())
}
fn run_bn_mul_fp12(f: Fp12<BN254>, g: Fp12<BN254>, label: &str) -> Vec<U256> {
fn run_bn_mul_fp12(f: Fp12<BN254>, g: Fp12<BN254>, label: &str) -> Fp12<BN254> {
let in0: usize = 100;
let in1: usize = 112;
let out: usize = 124;
@ -69,15 +57,15 @@ fn run_bn_mul_fp12(f: Fp12<BN254>, g: Fp12<BN254>, label: &str) -> Vec<U256> {
if label == "square_fp254_12" {
stack.remove(0);
}
let setup = InterpreterMemoryInitialization {
label: label.to_string(),
stack,
segment: BnPairing,
memory: vec![(in0, f.on_stack()), (in1, g.on_stack())],
memory: vec![(in0, f.to_stack().to_vec()), (in1, g.to_stack().to_vec())],
};
let interpreter = run_interpreter_with_memory(setup).unwrap();
interpreter.extract_kernel_memory(BnPairing, out..out + 12)
let output = interpreter.extract_kernel_memory(BnPairing, out..out + 12);
Fp12::<BN254>::from_stack(&output)
}
#[test]
@ -87,30 +75,27 @@ fn test_bn_mul_fp12() -> Result<()> {
let g: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
let h: Fp12<BN254> = gen_bn_fp12_sparse(&mut rng);
let out_normal: Vec<U256> = run_bn_mul_fp12(f, g, "mul_fp254_12");
let out_sparse: Vec<U256> = run_bn_mul_fp12(f, h, "mul_fp254_12_sparse");
let out_square: Vec<U256> = run_bn_mul_fp12(f, f, "square_fp254_12");
let output_normal = run_bn_mul_fp12(f, g, "mul_fp254_12");
let output_sparse = run_bn_mul_fp12(f, h, "mul_fp254_12_sparse");
let output_square = run_bn_mul_fp12(f, f, "square_fp254_12");
let exp_normal: Vec<U256> = (f * g).on_stack();
let exp_sparse: Vec<U256> = (f * h).on_stack();
let exp_square: Vec<U256> = (f * f).on_stack();
assert_eq!(out_normal, exp_normal);
assert_eq!(out_sparse, exp_sparse);
assert_eq!(out_square, exp_square);
assert_eq!(output_normal, f * g);
assert_eq!(output_sparse, f * h);
assert_eq!(output_square, f * f);
Ok(())
}
fn run_bn_frob_fp6(f: Fp6<BN254>, n: usize) -> Vec<U256> {
fn run_bn_frob_fp6(n: usize, f: Fp6<BN254>) -> Fp6<BN254> {
let setup = InterpreterMemoryInitialization {
label: format!("test_frob_fp254_6_{}", n),
stack: f.on_stack(),
stack: f.to_stack().to_vec(),
segment: BnPairing,
memory: vec![],
};
let interpreter = run_interpreter_with_memory(setup).unwrap();
extract_stack(interpreter)
let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap();
let output: Vec<U256> = interpreter.stack().iter().rev().cloned().collect();
Fp6::<BN254>::from_stack(&output)
}
#[test]
@ -118,34 +103,33 @@ fn test_bn_frob_fp6() -> Result<()> {
let mut rng = rand::thread_rng();
let f: Fp6<BN254> = rng.gen::<Fp6<BN254>>();
for n in 1..4 {
let output: Vec<U256> = run_bn_frob_fp6(f, n);
let expected: Vec<U256> = f.frob(n).on_stack();
assert_eq!(output, expected);
let output = run_bn_frob_fp6(n, f);
assert_eq!(output, f.frob(n));
}
Ok(())
}
fn run_bn_frob_fp12(f: Fp12<BN254>, n: usize) -> Vec<U256> {
fn run_bn_frob_fp12(f: Fp12<BN254>, n: usize) -> Fp12<BN254> {
let ptr: usize = 100;
let setup = InterpreterMemoryInitialization {
label: format!("test_frob_fp254_12_{}", n),
stack: vec![U256::from(ptr)],
segment: BnPairing,
memory: vec![(ptr, f.on_stack())],
memory: vec![(ptr, f.to_stack().to_vec())],
};
let interpreter = run_interpreter_with_memory(setup).unwrap();
interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12)
let interpeter: Interpreter = run_interpreter_with_memory(setup).unwrap();
let output: Vec<U256> = interpeter.extract_kernel_memory(BnPairing, ptr..ptr + 12);
Fp12::<BN254>::from_stack(&output)
}
#[test]
fn test_bn_frob_fp12() -> Result<()> {
fn test_frob_fp12() -> Result<()> {
let mut rng = rand::thread_rng();
let f: Fp12<BN254> = rng.gen::<Fp12<BN254>>();
for n in [1, 2, 3, 6] {
let output = run_bn_frob_fp12(f, n);
let expected: Vec<U256> = f.frob(n).on_stack();
assert_eq!(output, expected);
assert_eq!(output, f.frob(n));
}
Ok(())
}
@ -161,13 +145,13 @@ fn test_bn_inv_fp12() -> Result<()> {
label: "inv_fp254_12".to_string(),
stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)],
segment: BnPairing,
memory: vec![(ptr, f.on_stack())],
memory: vec![(ptr, f.to_stack().to_vec())],
};
let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap();
let output: Vec<U256> = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12);
let expected: Vec<U256> = f.inv().on_stack();
let output = Fp12::<BN254>::from_stack(&output);
assert_eq!(output, expected);
assert_eq!(output, f.inv());
Ok(())
}
@ -188,12 +172,12 @@ fn test_bn_final_exponent() -> Result<()> {
U256::from(0xdeadbeefu32),
],
segment: BnPairing,
memory: vec![(ptr, f.on_stack())],
memory: vec![(ptr, f.to_stack().to_vec())],
};
let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap();
let output: Vec<U256> = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12);
let expected: Vec<U256> = bn_final_exponent(f).on_stack();
let expected: Vec<U256> = bn_final_exponent(f).to_stack();
assert_eq!(output, expected);
@ -209,8 +193,8 @@ fn test_bn_miller() -> Result<()> {
let p: Curve<BN254> = rng.gen::<Curve<BN254>>();
let q: Curve<Fp2<BN254>> = rng.gen::<Curve<Fp2<BN254>>>();
let mut input = p.on_stack();
input.extend(q.on_stack());
let mut input = p.to_stack();
input.extend(q.to_stack());
let setup = InterpreterMemoryInitialization {
label: "bn254_miller".to_string(),
@ -220,7 +204,7 @@ fn test_bn_miller() -> Result<()> {
};
let interpreter = run_interpreter_with_memory(setup).unwrap();
let output: Vec<U256> = interpreter.extract_kernel_memory(BnPairing, out..out + 12);
let expected = bn_miller_loop(p, q).on_stack();
let expected = bn_miller_loop(p, q).to_stack();
assert_eq!(output, expected);
@ -243,13 +227,13 @@ fn test_bn_pairing() -> Result<()> {
let p: Curve<BN254> = Curve::<BN254>::int(m);
let q: Curve<Fp2<BN254>> = Curve::<Fp2<BN254>>::int(n);
input.extend(p.on_stack());
input.extend(q.on_stack());
input.extend(p.to_stack());
input.extend(q.to_stack());
}
let p: Curve<BN254> = Curve::<BN254>::int(acc);
let q: Curve<Fp2<BN254>> = Curve::<Fp2<BN254>>::GENERATOR;
input.extend(p.on_stack());
input.extend(q.on_stack());
input.extend(p.to_stack());
input.extend(q.to_stack());
let setup = InterpreterMemoryInitialization {
label: "bn254_pairing".to_string(),

View File

@ -25,12 +25,21 @@ impl<T: FieldExt> Curve<T> {
}
}
impl<T: FieldExt + Stack> Curve<T> {
pub fn on_stack(self) -> Vec<U256> {
let mut stack = self.x.on_stack();
stack.extend(self.y.on_stack());
impl<T: FieldExt + Stack> Stack for Curve<T> {
const SIZE: usize = 2 * T::SIZE;
fn to_stack(&self) -> Vec<U256> {
let mut stack = self.x.to_stack();
stack.extend(self.y.to_stack());
stack
}
fn from_stack(stack: &[U256]) -> Self {
Curve {
x: T::from_stack(&stack[0..T::SIZE]),
y: T::from_stack(&stack[T::SIZE..2 * T::SIZE]),
}
}
}
impl<T> Curve<T>

View File

@ -1223,43 +1223,79 @@ where
}
pub trait Stack {
fn on_stack(self) -> Vec<U256>;
const SIZE: usize;
fn to_stack(&self) -> Vec<U256>;
fn from_stack(stack: &[U256]) -> Self;
}
impl Stack for BN254 {
fn on_stack(self) -> Vec<U256> {
const SIZE: usize = 1;
fn to_stack(&self) -> Vec<U256> {
vec![self.val]
}
fn from_stack(stack: &[U256]) -> BN254 {
BN254 { val: stack[0] }
}
}
impl Stack for BLS381 {
fn on_stack(self) -> Vec<U256> {
const SIZE: usize = 2;
fn to_stack(&self) -> Vec<U256> {
vec![self.lo(), self.hi()]
}
fn from_stack(stack: &[U256]) -> BLS381 {
let mut val = [0u64; 8];
val[..4].copy_from_slice(&stack[0].0);
val[4..].copy_from_slice(&stack[1].0);
BLS381 { val: U512(val) }
}
}
impl<T> Stack for Fp2<T>
where
T: FieldExt + Stack,
{
fn on_stack(self) -> Vec<U256> {
let mut stack = self.re.on_stack();
stack.extend(self.im.on_stack());
impl<T: FieldExt + Stack> Stack for Fp2<T> {
const SIZE: usize = 2 * T::SIZE;
fn to_stack(&self) -> Vec<U256> {
let mut stack = self.re.to_stack();
stack.extend(self.im.to_stack());
stack
}
fn from_stack(stack: &[U256]) -> Fp2<T> {
let field_size = T::SIZE;
let re = T::from_stack(&stack[0..field_size]);
let im = T::from_stack(&stack[field_size..2 * field_size]);
Fp2 { re, im }
}
}
impl<T> Stack for Fp6<T>
where
T: FieldExt,
Fp2<T>: Adj + Stack,
Fp2<T>: Adj,
Fp2<T>: Stack,
{
fn on_stack(self) -> Vec<U256> {
let mut stack = self.t0.on_stack();
stack.extend(self.t1.on_stack());
stack.extend(self.t2.on_stack());
const SIZE: usize = 3 * Fp2::<T>::SIZE;
fn to_stack(&self) -> Vec<U256> {
let mut stack = self.t0.to_stack();
stack.extend(self.t1.to_stack());
stack.extend(self.t2.to_stack());
stack
}
fn from_stack(stack: &[U256]) -> Self {
let field_size = Fp2::<T>::SIZE;
let t0 = Fp2::<T>::from_stack(&stack[0..field_size]);
let t1 = Fp2::<T>::from_stack(&stack[field_size..2 * field_size]);
let t2 = Fp2::<T>::from_stack(&stack[2 * field_size..3 * field_size]);
Fp6 { t0, t1, t2 }
}
}
impl<T> Stack for Fp12<T>
@ -1268,9 +1304,18 @@ where
Fp2<T>: Adj,
Fp6<T>: Stack,
{
fn on_stack(self) -> Vec<U256> {
let mut stack = self.z0.on_stack();
stack.extend(self.z1.on_stack());
const SIZE: usize = 2 * Fp6::<T>::SIZE;
fn to_stack(&self) -> Vec<U256> {
let mut stack = self.z0.to_stack();
stack.extend(self.z1.to_stack());
stack
}
fn from_stack(stack: &[U256]) -> Self {
let field_size = Fp6::<T>::SIZE;
let z0 = Fp6::<T>::from_stack(&stack[0..field_size]);
let z1 = Fp6::<T>::from_stack(&stack[field_size..2 * field_size]);
Fp12 { z0, z1 }
}
}