Skip to content

Commit

Permalink
feat: g2_add implementation (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
fgimenez committed Apr 4, 2024
1 parent 0e5e671 commit aa8da86
Show file tree
Hide file tree
Showing 2 changed files with 824 additions and 24 deletions.
139 changes: 115 additions & 24 deletions crates/precompile/src/bls12_381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ use crate::addresses::{
use blst::{
blst_bendian_from_fp, blst_fp, blst_fp_from_bendian, blst_p1, blst_p1_add_or_double_affine,
blst_p1_affine, blst_p1_affine_in_g1, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine,
blst_scalar, blst_scalar_from_bendian, p1_affines,
blst_p2, blst_p2_add_or_double_affine, blst_p2_affine, blst_p2_affine_in_g2,
blst_p2_from_affine, blst_p2_to_affine, blst_scalar, blst_scalar_from_bendian, p1_affines,
};
use revm_precompile::{u64_to_address, Precompile, PrecompileWithAddress};
use revm_primitives::{Bytes, PrecompileError, PrecompileResult, B256};

const G1ADD_BASE: u64 = 500;
const G1MUL_BASE: u64 = 12000;
const G1ADD_INPUT_LENGTH: usize = 256;
const G1_INPUT_ITEM_LENGTH: usize = 128;
const G1_OUTPUT_LENGTH: usize = 128;
const G1MUL_BASE: u64 = 12000;
const G1MUL_INPUT_LENGTH: usize = 160;
const G1MUL_OUTPUT_LENGTH: usize = 256;
const INPUT_ITEM_LENGTH: usize = 128;
const OUTPUT_LENGTH: usize = 128;
const NBITS: usize = 256;
const G2ADD_BASE: u64 = 800;
const G2ADD_INPUT_LENGTH: usize = 512;
const G2_INPUT_ITEM_LENGTH: usize = 256;
const G2_OUTPUT_LENGTH: usize = 256;
const FP_LENGTH: usize = 48;
const PADDED_INPUT_LENGTH: usize = 64;
const PADDING_LENGTH: usize = 16;
Expand Down Expand Up @@ -62,6 +67,16 @@ fn encode_g1_point(out: &mut [u8], input: *const blst_p1_affine) {
}
}

fn encode_g2_point(out: &mut [u8], input: *const blst_p2_affine) {
// SAFETY: out comes from fixed length array, input is a blst value.
unsafe {
fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &(*input).x.fp[0]);
fp_to_bytes(&mut out[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH], &(*input).x.fp[1]);
fp_to_bytes(&mut out[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH], &(*input).y.fp[0]);
fp_to_bytes(&mut out[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH], &(*input).y.fp[1]);
}
}

fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) {
if out.len() != PADDED_FP_LENGTH {
return;
Expand Down Expand Up @@ -116,9 +131,9 @@ fn extract_g1_input(
out: *mut blst_p1_affine,
input: &[u8],
) -> Result<*mut blst_p1_affine, PrecompileError> {
if input.len() != INPUT_ITEM_LENGTH {
if input.len() != G1_INPUT_ITEM_LENGTH {
return Err(PrecompileError::Other(format!(
"Input should be {INPUT_ITEM_LENGTH} bits, was {}",
"Input should be {G1_INPUT_ITEM_LENGTH} bits, was {}",
input.len()
)));
}
Expand All @@ -127,7 +142,7 @@ fn extract_g1_input(
Ok(input_p0_x) => input_p0_x,
Err(e) => return Err(e),
};
let input_p0_y = match remove_padding(&input[PADDED_INPUT_LENGTH..INPUT_ITEM_LENGTH]) {
let input_p0_y = match remove_padding(&input[PADDED_INPUT_LENGTH..G1_INPUT_ITEM_LENGTH]) {
Ok(input_p0_y) => input_p0_y,
Err(e) => return Err(e),
};
Expand All @@ -146,6 +161,43 @@ fn extract_g1_input(
Ok(out)
}

// Extracts a G2 point in Affine format from a 256 byte slice representation.
fn extract_g2_input(
out: *mut blst_p2_affine,
input: &[u8],
) -> Result<*mut blst_p2_affine, PrecompileError> {
if input.len() != G2_INPUT_ITEM_LENGTH {
return Err(PrecompileError::Other(format!(
"Input should be {G2_INPUT_ITEM_LENGTH} bits, was {}",
input.len()
)));
}

let mut input_fps: [[u8; FP_LENGTH]; 4] = [[0; FP_LENGTH]; 4];
for i in 0..4 {
input_fps[i] =
match remove_padding(&input[i * PADDED_INPUT_LENGTH..(i + 1) * PADDED_INPUT_LENGTH]) {
Ok(fp_0) => fp_0,
Err(e) => return Err(e),
};
}

// SAFETY: items in fps have fixed length, out is a blst value.
unsafe {
blst_fp_from_bendian(&mut (*out).x.fp[0], input_fps[0].as_ptr());
blst_fp_from_bendian(&mut (*out).x.fp[1], input_fps[1].as_ptr());
blst_fp_from_bendian(&mut (*out).y.fp[0], input_fps[2].as_ptr());
blst_fp_from_bendian(&mut (*out).y.fp[1], input_fps[3].as_ptr());
}
// SAFETY: out is a blst value.
unsafe {
if !blst_p2_affine_in_g2(out) {
return Err(PrecompileError::Other("Element not in G2".to_string()));
}
}
Ok(out)
}

/// G1 addition call expects `256` bytes as an input that is interpreted as byte
/// concatenation of two G1 points (`128` bytes each).
/// Output is an encoding of addition operation result - single G1 point (`128`
Expand All @@ -165,10 +217,10 @@ pub fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult {
}

let mut a_aff: blst_p1_affine = Default::default();
let a_aff = extract_g1_input(&mut a_aff, &input[..INPUT_ITEM_LENGTH])?;
let a_aff = extract_g1_input(&mut a_aff, &input[..G1_INPUT_ITEM_LENGTH])?;

let mut b_aff: blst_p1_affine = Default::default();
let b_aff = extract_g1_input(&mut b_aff, &input[INPUT_ITEM_LENGTH..])?;
let b_aff = extract_g1_input(&mut b_aff, &input[G1_INPUT_ITEM_LENGTH..])?;

let mut b: blst_p1 = Default::default();
// SAFETY: b and b_aff are blst values.
Expand All @@ -188,7 +240,7 @@ pub fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult {
blst_p1_to_affine(&mut p_aff, &p);
}

let mut out = [0u8; OUTPUT_LENGTH];
let mut out = [0u8; G1_OUTPUT_LENGTH];
encode_g1_point(&mut out, &p_aff);

Ok((G1ADD_BASE, out.into()))
Expand Down Expand Up @@ -217,27 +269,27 @@ pub fn g1_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult {
}

let mut p0_aff: blst_p1_affine = Default::default();
let p0_aff = extract_g1_input(&mut p0_aff, &input[..INPUT_ITEM_LENGTH])?;
let p0_aff = extract_g1_input(&mut p0_aff, &input[..G1_INPUT_ITEM_LENGTH])?;
let mut p0: blst_p1 = Default::default();
// SAFETY: p0 and p0_aff are blst values.
unsafe {
blst_p1_from_affine(&mut p0, p0_aff);
}

let input_scalar0 = extract_scalar_input(&input[INPUT_ITEM_LENGTH..])?;
let input_scalar0 = extract_scalar_input(&input[G1_INPUT_ITEM_LENGTH..])?;

let mut p: blst_p1 = Default::default();
// SAFETY: input_scalar0.b has fixed size, p and p0 are blst values.
unsafe {
blst_p1_mult(&mut p, &p0, input_scalar0.b.as_ptr(), G1MUL_OUTPUT_LENGTH);
blst_p1_mult(&mut p, &p0, input_scalar0.b.as_ptr(), NBITS);
}
let mut p_aff: blst_p1_affine = Default::default();
// SAFETY: p_aff and p are blst values.
unsafe {
blst_p1_to_affine(&mut p_aff, &p);
}

let mut out = [0u8; OUTPUT_LENGTH];
let mut out = [0u8; G1_OUTPUT_LENGTH];
encode_g1_point(&mut out, &p_aff);

Ok((G1MUL_BASE, out.into()))
Expand Down Expand Up @@ -296,7 +348,7 @@ fn g1_multiexp(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let mut p0_aff: blst_p1_affine = Default::default();
let p0_aff = extract_g1_input(
&mut p0_aff,
&input[i * G1MUL_INPUT_LENGTH..i * G1MUL_INPUT_LENGTH + INPUT_ITEM_LENGTH],
&input[i * G1MUL_INPUT_LENGTH..i * G1MUL_INPUT_LENGTH + G1_INPUT_ITEM_LENGTH],
)?;
let mut p0: blst_p1 = Default::default();
// SAFETY: p0 and p0_aff are blst values.
Expand All @@ -308,23 +360,23 @@ fn g1_multiexp(input: &Bytes, gas_limit: u64) -> PrecompileResult {

scalars.extend_from_slice(
&extract_scalar_input(
&input[i * G1MUL_INPUT_LENGTH + INPUT_ITEM_LENGTH
..i * G1MUL_INPUT_LENGTH + INPUT_ITEM_LENGTH + SCALAR_LENGTH],
&input[i * G1MUL_INPUT_LENGTH + G1_INPUT_ITEM_LENGTH
..i * G1MUL_INPUT_LENGTH + G1_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
)?
.b,
);
}

let points = p1_affines::from(&g1_points);
let multiexp = points.mult(&scalars, G1MUL_OUTPUT_LENGTH);
let multiexp = points.mult(&scalars, NBITS);

let mut multiexp_aff: blst_p1_affine = Default::default();
// SAFETY: multiexp_aff and multiexp are blst values.
unsafe {
blst_p1_to_affine(&mut multiexp_aff, &multiexp);
}

let mut out = [0u8; OUTPUT_LENGTH];
let mut out = [0u8; G1_OUTPUT_LENGTH];
encode_g1_point(&mut out, &multiexp_aff);

Ok((required_gas, out.into()))
Expand All @@ -334,13 +386,51 @@ fn g1_multiexp(input: &Bytes, gas_limit: u64) -> PrecompileResult {
const BLS12_G2ADD: PrecompileWithAddress =
PrecompileWithAddress(u64_to_address(BLS12_G2ADD_ADDRESS), Precompile::Standard(g2_add));

fn g2_add(_input: &Bytes, gas_limit: u64) -> PrecompileResult {
const G2ADD_BASE: u64 = 800;
/// G2 addition call expects `512` bytes as an input that is interpreted as byte
/// concatenation of two G2 points (`256` bytes each).
///
/// > Output is an encoding of addition operation result - single G2 point (`256`
/// bytes).
fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult {
if G2ADD_BASE > gas_limit {
return Err(PrecompileError::OutOfGas);
}
let result = 1;
Ok((G2ADD_BASE, B256::with_last_byte(result as u8).into()))

if input.len() != G2ADD_INPUT_LENGTH {
return Err(PrecompileError::Other(format!(
"G2ADD Input should be {G2ADD_INPUT_LENGTH} bits, was {}",
input.len()
)));
}

let mut a_aff: blst_p2_affine = Default::default();
let a_aff = extract_g2_input(&mut a_aff, &input[..G2_INPUT_ITEM_LENGTH])?;

let mut b_aff: blst_p2_affine = Default::default();
let b_aff = extract_g2_input(&mut b_aff, &input[G2_INPUT_ITEM_LENGTH..])?;

let mut b: blst_p2 = Default::default();
// SAFETY: b and b_aff are blst values.
unsafe {
blst_p2_from_affine(&mut b, b_aff);
}

let mut p: blst_p2 = Default::default();
// SAFETY: p, b and a_aff are blst values.
unsafe {
blst_p2_add_or_double_affine(&mut p, &b, a_aff);
}

let mut p_aff: blst_p2_affine = Default::default();
// SAFETY: p_aff and p are blst values.
unsafe {
blst_p2_to_affine(&mut p_aff, &p);
}

let mut out = [0u8; G2_OUTPUT_LENGTH];
encode_g2_point(&mut out, &p_aff);

Ok((G2ADD_BASE, out.into()))
}

/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MUL precompile.
Expand Down Expand Up @@ -448,6 +538,7 @@ mod test {
#[case::g1_add(g1_add, "blsG1Add.json")]
#[case::g1_mul(g1_mul, "blsG1Mul.json")]
#[case::g1_multiexp(g1_multiexp, "blsG1MultiExp.json")]
#[case::g2_add(g2_add, "blsG2Add.json")]
fn test_bls(
#[case] precompile: fn(input: &Bytes, gas_limit: u64) -> PrecompileResult,
#[case] file_name: &str,
Expand Down
Loading

0 comments on commit aa8da86

Please sign in to comment.