diff --git a/Cargo.lock b/Cargo.lock index ece8043aa7..66e2120fbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -663,6 +663,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", + "rayon", "zeroize", ] @@ -721,6 +722,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "paste", + "rayon", "zeroize", ] @@ -868,6 +870,7 @@ dependencies = [ "arrayvec", "digest 0.10.7", "num-bigint 0.4.6", + "rayon", ] [[package]] @@ -909,6 +912,7 @@ checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", "rand", + "rayon", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 99fc80b548..8d8669d2d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index e39b83f667..e4de35726d 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -102,8 +102,11 @@ std = [ "serde_json/std", "ark-bn254/std", "ark-bls12-381/std", + "ark-bls12-381/std", "ark-ec/std", + "ark-ec/parallel", "ark-ff/std", + "ark-ff/parallel", "ark-serialize/std", ] hashbrown = ["primitives/hashbrown"] diff --git a/crates/precompile/benches/bench.rs b/crates/precompile/benches/bench.rs index 6a650f3fb3..150f8ab2eb 100644 --- a/crates/precompile/benches/bench.rs +++ b/crates/precompile/benches/bench.rs @@ -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::{ @@ -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()) }); diff --git a/crates/precompile/benches/eip2537.rs b/crates/precompile/benches/eip2537.rs new file mode 100644 index 0000000000..fecdced4d8 --- /dev/null +++ b/crates/precompile/benches/eip2537.rs @@ -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; + +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(num_points: usize, rng: &mut StdRng) -> Vec

{ + let mut points = Vec::new(); + for _ in 0..num_points { + points.push(P::rand(rng)); + } + points + } + + pub(super) fn random_field(num_scalars: usize, rng: &mut StdRng) -> Vec { + 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(field: &F) -> Vec { + 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 { + 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 { + let num_g1_points = num_test_vectors * 2; + let points: Vec = 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 { + let num_g2_points = num_test_vectors * 2; + let points: Vec = 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(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(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(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 = random_points(msm_size, rng); + let scalars: Vec = 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 = random_points(msm_size, rng); + let scalars: Vec = 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(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 = random_points(num_pairs, rng); + let g2_points: Vec = 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(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(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(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()); + }); +} diff --git a/crates/precompile/src/bls12_381.rs b/crates/precompile/src/bls12_381.rs index c8ead30c39..a2e56c9259 100644 --- a/crates/precompile/src/bls12_381.rs +++ b/crates/precompile/src/bls12_381.rs @@ -1,14 +1,14 @@ use crate::PrecompileWithAddress; -cfg_if::cfg_if! { - if #[cfg(feature = "blst")]{ - mod blst; - use blst as crypto_backend; - } else { - mod arkworks; - use arkworks as crypto_backend; - } -} +// cfg_if::cfg_if! { +// if #[cfg(feature = "blst")]{ +// mod blst; +// use blst as crypto_backend; +// } else { +// } +// } +mod arkworks; +use arkworks as crypto_backend; pub mod g1_add; pub mod g1_msm; diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index b5655109f7..4146ff86c0 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -43,6 +43,7 @@ use kzg_rs as _; // silence arkworks-bls12-381 lint as blst will be used as default if both are enabled. cfg_if::cfg_if! { if #[cfg(feature = "blst")]{ + use blst as _; use ark_bls12_381 as _; use ark_ff as _; use ark_ec as _;