Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add benchmarks for ecpairing, kzg, ecrecover precompiles #1211

Merged
merged 3 commits into from
Mar 19, 2024
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: 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
Loading