Skip to content

Commit

Permalink
feat: add benchmarks for ecpairing, kzg, ecrecover precompiles (#1211)
Browse files Browse the repository at this point in the history
* feat: basic crypto benches

* cleanup

* more cleanup
  • Loading branch information
Rjected authored Mar 19, 2024
1 parent 5aff229 commit 97a4840
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions crates/precompile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ k256 = { version = "0.13.3", default-features = false, features = ["ecdsa"] }
secp256k1 = { version = "0.28.2", default-features = false, features = [
"alloc",
"recovery",
"rand",
"global-context",
], optional = true }

[dev-dependencies]
criterion = { version = "0.5" }

[features]
default = ["std", "c-kzg", "secp256k1", "portable"]
Expand Down Expand Up @@ -65,3 +69,8 @@ portable = ["revm-primitives/portable", "c-kzg?/portable"]
# The problem that `secp256k1` has is it fails to build for `wasm` target on Windows and Mac as it is c lib.
# In Linux it passes. If you don't require to build wasm on win/mac, it is safe to use it and it is enabled by default.
secp256k1 = ["dep:secp256k1"]

[[bench]]
name = "bench"
path = "benches/bench.rs"
harness = false
127 changes: 127 additions & 0 deletions crates/precompile/benches/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use revm_precompile::{
bn128::{
pair::{ISTANBUL_PAIR_BASE, ISTANBUL_PAIR_PER_POINT},
run_pair,
},
kzg_point_evaluation::run,
secp256k1::ec_recover_run,
Bytes,
};
use revm_primitives::{hex, keccak256, Env, U256, VERSIONED_HASH_VERSION_KZG};
use secp256k1::{rand, Message, SecretKey, SECP256K1};
use sha2::{Digest, Sha256};

/// Benchmarks different cryptography-related precompiles.
pub fn benchmark_crypto_precompiles(c: &mut Criterion) {
let mut group = c.benchmark_group("Crypto Precompile benchmarks");
let group_name = |description: &str| format!("precompile bench | {description}");

// === ECPAIRING ===

// set up ecpairing input
let input = hex::decode(
"\
1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
)
.unwrap();

let res = run_pair(
&input,
ISTANBUL_PAIR_PER_POINT,
ISTANBUL_PAIR_BASE,
u64::MAX,
)
.unwrap()
.0;

println!("gas used by regular pairing call: {:?}", res);

// === ECRECOVER ===

// generate secp256k1 signature
let data = hex::decode("1337133713371337").unwrap();
let hash = keccak256(data);
let secret_key = SecretKey::new(&mut rand::thread_rng());

let message = Message::from_digest_slice(&hash[..]).unwrap();
let s = SECP256K1.sign_ecdsa_recoverable(&message, &secret_key);
let (rec_id, data) = s.serialize_compact();
let mut rec_id = rec_id.to_i32() as u8;
assert_eq!(rec_id, 0);
rec_id += 27;

let mut message_and_signature = [0u8; 128];
message_and_signature[0..32].copy_from_slice(&hash[..]);

// fit signature into format the precompile expects
let rec_id = U256::from(rec_id as u64);
message_and_signature[32..64].copy_from_slice(&rec_id.to_be_bytes::<32>());
message_and_signature[64..128].copy_from_slice(&data);

let message_and_signature = Bytes::from(message_and_signature);
let gas = ec_recover_run(&message_and_signature, u64::MAX).unwrap();
println!("gas used by ecrecover precompile: {:?}", gas);

// === POINT_EVALUATION ===

// now check kzg precompile gas
let commitment = hex!("8f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7").to_vec();
let mut versioned_hash = Sha256::digest(&commitment).to_vec();
versioned_hash[0] = VERSIONED_HASH_VERSION_KZG;
let z = hex!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000").to_vec();
let y = hex!("1522a4a7f34e1ea350ae07c29c96c7e79655aa926122e95fe69fcbd932ca49e9").to_vec();
let proof = hex!("a62ad71d14c5719385c0686f1871430475bf3a00f0aa3f7b8dd99a9abc2160744faf0070725e00b60ad9a026a15b1a8c").to_vec();

let kzg_input = [versioned_hash, z, y, commitment, proof].concat().into();

let gas = 50000;
let env = Env::default();
let (actual_gas, _actual_output) = run(&kzg_input, gas, &env).unwrap();
println!("gas used by kzg precompile: {:?}", actual_gas);

group.bench_function(group_name("ecrecover precompile"), |b| {
b.iter(|| {
ec_recover_run(&message_and_signature, u64::MAX).unwrap();
black_box(())
})
});

group.bench_function(group_name("ecpairing precompile"), |b| {
b.iter(|| {
run_pair(
&input,
ISTANBUL_PAIR_PER_POINT,
ISTANBUL_PAIR_BASE,
u64::MAX,
)
.unwrap();
black_box(())
})
});

group.bench_function(group_name("kzg precompile"), |b| {
b.iter(|| {
run(&kzg_input, gas, &env).unwrap();
black_box(())
})
});
}

criterion_group! {
name = benches;
config = Criterion::default();
targets = benchmark_crypto_precompiles
}
criterion_main!(benches);
6 changes: 3 additions & 3 deletions crates/precompile/src/bn128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ pub mod pair {

const ADDRESS: Address = crate::u64_to_address(8);

const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
const ISTANBUL_PAIR_BASE: u64 = 45_000;
pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
pub const ISTANBUL_PAIR_BASE: u64 = 45_000;
pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress(
ADDRESS,
Precompile::Standard(|input, gas_limit| {
Expand Down Expand Up @@ -170,7 +170,7 @@ fn run_mul(input: &[u8]) -> Result<Bytes, Error> {
Ok(out.into())
}

fn run_pair(
pub fn run_pair(
input: &[u8],
pair_per_point_cost: u64,
pair_base_cost: u64,
Expand Down
2 changes: 1 addition & 1 deletion crates/precompile/src/kzg_point_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const RETURN_VALUE: &[u8; 64] = &hex!(
/// | versioned_hash | z | y | commitment | proof |
/// | 32 | 32 | 32 | 48 | 48 |
/// with z and y being padded 32 byte big endian values
fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
pub fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
if gas_limit < GAS_COST {
return Err(Error::OutOfGas);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/precompile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Implementations of EVM precompiled contracts.
#![warn(rustdoc::all)]
#![warn(unused_crate_dependencies)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(not(feature = "std"), no_std)]

Expand All @@ -11,7 +11,7 @@
extern crate alloc as std;

mod blake2;
mod bn128;
pub mod bn128;
mod hash;
mod identity;
#[cfg(feature = "c-kzg")]
Expand Down
2 changes: 1 addition & 1 deletion crates/precompile/src/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ mod secp256k1 {
}
}

fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
const ECRECOVER_BASE: u64 = 3_000;

if ECRECOVER_BASE > gas_limit {
Expand Down

0 comments on commit 97a4840

Please sign in to comment.