Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ alloy-signer-local = { version = "0.12.4", default-features = false }
alloy-transport = { version = "0.12.4", default-features = false }

# precompiles
ark-bn254 = { version = "0.5", default-features = false }
ark-bls12-381 = { version = "0.5", default-features = false }
ark-bn254 = { version = "0.5", default-features = false }
ark-ec = { version = "0.5", default-features = false }
ark-ff = { version = "0.5", default-features = false }
ark-serialize = { version = "0.5", default-features = false }
Expand Down
10 changes: 10 additions & 0 deletions crates/precompile/benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod eip2537;

use criterion::{criterion_group, criterion_main, Criterion};
use primitives::{eip4844::VERSIONED_HASH_VERSION_KZG, hex, keccak256, Bytes, U256};
use revm_precompile::{
Expand Down Expand Up @@ -122,6 +124,14 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) {
let output = run(&kzg_input, gas).unwrap();
println!("gas used by kzg precompile: {:?}", output.gas_used);

eip2537::add_g1_add_benches(&mut group);
eip2537::add_g2_add_benches(&mut group);
eip2537::add_g1_msm_benches(&mut group);
eip2537::add_g2_msm_benches(&mut group);
eip2537::add_pairing_benches(&mut group);
eip2537::add_map_fp_to_g1_benches(&mut group);
eip2537::add_map_fp2_to_g2_benches(&mut group);

group.bench_function(group_name("ecrecover precompile"), |b| {
b.iter(|| ec_recover_run(&message_and_signature, u64::MAX).unwrap())
});
Expand Down
305 changes: 305 additions & 0 deletions crates/precompile/benches/eip2537.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
use ark_bls12_381::{Fq, Fr, G1Affine, G2Affine};
use ark_ec::AffineRepr;
use arkworks_general::{encode_base_field, encode_field_32_bytes, random_field, random_points};
use criterion::{measurement::Measurement, BenchmarkGroup};
use primitives::Bytes;
use rand::{rngs::StdRng, SeedableRng};
use revm_precompile::bls12_381_const::{PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH};

const RNG_SEED: u64 = 42;
const MAX_MSM_SIZE: usize = 256;
const MAX_PAIRING_PAIRS: usize = 16;

type PrecompileInput = Vec<u8>;

mod arkworks_general {
use ark_bls12_381::Fq;
use ark_ec::AffineRepr;
use ark_ff::Field;

use ark_serialize::CanonicalSerialize;
use rand::rngs::StdRng;
use revm_precompile::bls12_381_const::{FP_LENGTH, FP_PAD_BY, PADDED_FP_LENGTH};

pub(super) fn random_points<P: AffineRepr>(num_points: usize, rng: &mut StdRng) -> Vec<P> {
let mut points = Vec::new();
for _ in 0..num_points {
points.push(P::rand(rng));
}
points
}

pub(super) fn random_field<F: Field>(num_scalars: usize, rng: &mut StdRng) -> Vec<F> {
let mut points = Vec::new();
for _ in 0..num_scalars {
points.push(F::rand(rng));
}
points
}

// Note: This is kept separate from encode_base_field since it's for Fr scalars (32 bytes)
// while encode_base_field is for Fq field elements (padded to 64 bytes)
pub(super) fn encode_field_32_bytes<F: Field>(field: &F) -> Vec<u8> {
let mut bytes_be = vec![0u8; 32];
field
.serialize_uncompressed(&mut bytes_be[..])
.expect("Failed to serialize field element");
bytes_be.reverse();

bytes_be
}

// Add padding to Fq field element and convert it to big endian (BE) format
pub(super) fn encode_base_field(fp: &Fq) -> Vec<u8> {
let mut bytes = [0u8; FP_LENGTH];
fp.serialize_uncompressed(&mut bytes[..])
.expect("Failed to serialize field element");
bytes.reverse(); // Convert to big endian

// Add padding
let mut padded_bytes = vec![0; PADDED_FP_LENGTH];
padded_bytes[FP_PAD_BY..PADDED_FP_LENGTH].copy_from_slice(&bytes);

padded_bytes
}
}

// Note: This has been copied in from precompile/src/bls12_381 since
// those are not public
pub fn encode_bls12381_g1_point(input: &G1Affine) -> [u8; PADDED_G1_LENGTH] {
let mut output = [0u8; PADDED_G1_LENGTH];

let Some((x, y)) = input.xy() else {
return output; // Point at infinity, return all zeros
};

let x_encoded = encode_base_field(&x);
let y_encoded = encode_base_field(&y);

// Copy the encoded values to the output
output[..PADDED_FP_LENGTH].copy_from_slice(&x_encoded);
output[PADDED_FP_LENGTH..].copy_from_slice(&y_encoded);

output
}
pub fn encode_bls12381_g2_point(input: &G2Affine) -> [u8; PADDED_G2_LENGTH] {
let mut output = [0u8; PADDED_G2_LENGTH];

let Some((x, y)) = input.xy() else {
return output; // Point at infinity, return all zeros
};

let x_c0_encoded = encode_base_field(&x.c0);
let x_c1_encoded = encode_base_field(&x.c1);
let y_c0_encoded = encode_base_field(&y.c0);
let y_c1_encoded = encode_base_field(&y.c1);

// Copy encoded values to output
output[..PADDED_FP_LENGTH].copy_from_slice(&x_c0_encoded);
output[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH].copy_from_slice(&x_c1_encoded);
output[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH].copy_from_slice(&y_c0_encoded);
output[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH].copy_from_slice(&y_c1_encoded);

output
}

fn g1_add_test_vectors(num_test_vectors: usize, rng: &mut StdRng) -> Vec<PrecompileInput> {
let num_g1_points = num_test_vectors * 2;
let points: Vec<G1Affine> = random_points(num_g1_points, rng);

points
.chunks_exact(2)
.map(|chunk| {
let lhs = chunk[0];
let rhs = chunk[1];
let mut g1_add_input = Vec::new();
g1_add_input.extend(encode_bls12381_g1_point(&lhs));
g1_add_input.extend(encode_bls12381_g1_point(&rhs));
g1_add_input
})
.collect()
}

fn g2_add_test_vectors(num_test_vectors: usize, rng: &mut StdRng) -> Vec<PrecompileInput> {
let num_g2_points = num_test_vectors * 2;
let points: Vec<G2Affine> = random_points(num_g2_points, rng);

points
.chunks_exact(2)
.map(|chunk| {
let lhs = chunk[0];
let rhs = chunk[1];
let mut g2_add_input = Vec::new();
g2_add_input.extend(encode_bls12381_g2_point(&lhs));
g2_add_input.extend(encode_bls12381_g2_point(&rhs));
g2_add_input
})
.collect()
}

pub fn add_g1_add_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::g1_add::PRECOMPILE;

let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vectors = g1_add_test_vectors(1, &mut rng);
let input = Bytes::from(test_vectors[0].clone());

let precompile = *PRECOMPILE.precompile();

group.bench_function("g1_add", |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}

pub fn add_g2_add_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::g2_add::PRECOMPILE;

let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vectors = g2_add_test_vectors(1, &mut rng);
let input = Bytes::from(test_vectors[0].clone());

let precompile = *PRECOMPILE.precompile();

group.bench_function("g2_add", |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}

pub fn add_g1_msm_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::g1_msm::PRECOMPILE;

let precompile = *PRECOMPILE.precompile();

let sizes_to_bench = [MAX_MSM_SIZE, MAX_MSM_SIZE / 2, 2, 1];

for size in sizes_to_bench {
let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vector = g1_msm_test_vectors(size, &mut rng);
let input = Bytes::from(test_vector);

group.bench_function(format!("g1_msm (size {})", size), |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}
}

fn g1_msm_test_vectors(msm_size: usize, rng: &mut StdRng) -> PrecompileInput {
let points: Vec<G1Affine> = random_points(msm_size, rng);
let scalars: Vec<Fr> = random_field(msm_size, rng);

let mut input = Vec::new();
for (point, scalar) in points.iter().zip(scalars.iter()) {
input.extend(encode_bls12381_g1_point(point));
input.extend(encode_field_32_bytes(scalar));
}

input
}

fn g2_msm_test_vectors(msm_size: usize, rng: &mut StdRng) -> PrecompileInput {
let points: Vec<G2Affine> = random_points(msm_size, rng);
let scalars: Vec<Fr> = random_field(msm_size, rng);

let mut input = Vec::new();
for (point, scalar) in points.iter().zip(scalars.iter()) {
input.extend(encode_bls12381_g2_point(point));
input.extend(encode_field_32_bytes(scalar));
}

input
}

pub fn add_g2_msm_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::g2_msm::PRECOMPILE;

let precompile = *PRECOMPILE.precompile();

let sizes_to_bench = [MAX_MSM_SIZE, MAX_MSM_SIZE / 2, 2, 1];

for size in sizes_to_bench {
let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vector = g2_msm_test_vectors(size, &mut rng);
let input = Bytes::from(test_vector);

group.bench_function(format!("g2_msm (size {})", size), |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}
}

fn pairing_test_vectors(num_pairs: usize, rng: &mut StdRng) -> PrecompileInput {
// Generate random G1 and G2 points for pairing
let g1_points: Vec<G1Affine> = random_points(num_pairs, rng);
let g2_points: Vec<G2Affine> = random_points(num_pairs, rng);

let mut input = Vec::new();
for (g1, g2) in g1_points.iter().zip(g2_points.iter()) {
input.extend(encode_bls12381_g1_point(g1));
input.extend(encode_bls12381_g2_point(g2));
}

input
}

pub fn add_pairing_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::pairing::PRECOMPILE;

let precompile = *PRECOMPILE.precompile();

let sizes_to_bench = [MAX_PAIRING_PAIRS, MAX_PAIRING_PAIRS / 2, 2, 1];

for pairs in sizes_to_bench {
let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vector = pairing_test_vectors(pairs, &mut rng);
let input = Bytes::from(test_vector);

group.bench_function(format!("pairing ({} pairs)", pairs), |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}
}

fn map_fp_to_g1_test_vectors(rng: &mut StdRng) -> PrecompileInput {
let fp: Fq = random_field(1, rng)[0];
encode_base_field(&fp)
}

pub fn add_map_fp_to_g1_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::map_fp_to_g1::PRECOMPILE;

let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vector = map_fp_to_g1_test_vectors(&mut rng);
let input = Bytes::from(test_vector);

let precompile = *PRECOMPILE.precompile();

group.bench_function("map_fp_to_g1", |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}

fn map_fp2_to_g2_test_vectors(rng: &mut StdRng) -> PrecompileInput {
let fp_c0: Fq = random_field(1, rng)[0];
let fp_c1: Fq = random_field(1, rng)[0];

let mut input = Vec::new();

input.extend(encode_base_field(&fp_c0));
input.extend(encode_base_field(&fp_c1));

input
}

pub fn add_map_fp2_to_g2_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
use revm_precompile::bls12_381::map_fp2_to_g2::PRECOMPILE;

let mut rng = StdRng::seed_from_u64(RNG_SEED);
let test_vector = map_fp2_to_g2_test_vectors(&mut rng);
let input = Bytes::from(test_vector);

let precompile = *PRECOMPILE.precompile();

group.bench_function("map_fp2_to_g2", |b| {
b.iter(|| precompile(&input, u64::MAX).unwrap());
});
}
Loading