From 0651044b7ad7e8eaacf6650d64abb7a927e867f3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:39:24 +0200 Subject: [PATCH] perf(precompile): use `Bytes` in precompile functions (#1085) * perf(precompile): use `Bytes` in precompile functions * fix: ecrecover * chore: clippy --- crates/interpreter/src/instructions/macros.rs | 4 +- crates/precompile/src/blake2.rs | 8 ++-- crates/precompile/src/bn128.rs | 30 ++++++------ crates/precompile/src/hash.rs | 11 +++-- crates/precompile/src/identity.rs | 5 +- crates/precompile/src/kzg_point_evaluation.rs | 10 ++-- crates/precompile/src/modexp.rs | 47 +++++++++---------- crates/precompile/src/secp256k1.rs | 12 ++--- crates/precompile/src/utilities.rs | 13 ++--- crates/primitives/src/precompile.rs | 8 ++-- crates/revm/src/context.rs | 6 +-- 11 files changed, 74 insertions(+), 80 deletions(-) diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index fe8bf79758..ec6e6cb775 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -210,7 +210,7 @@ macro_rules! as_u64_saturated { macro_rules! as_usize_saturated { ($v:expr) => { - ::core::convert::TryInto::::try_into(as_u64_saturated!($v)).unwrap_or(usize::MAX) + usize::try_from(as_u64_saturated!($v)).unwrap_or(usize::MAX) }; } @@ -234,7 +234,7 @@ macro_rules! as_usize_or_fail_ret { $interp.instruction_result = $reason; return $ret; } - let Ok(val) = ::core::convert::TryInto::::try_into(x[0]) else { + let Ok(val) = usize::try_from(x[0]) else { $interp.instruction_result = $reason; return $ret; }; diff --git a/crates/precompile/src/blake2.rs b/crates/precompile/src/blake2.rs index 211b898cac..6a173494e9 100644 --- a/crates/precompile/src/blake2.rs +++ b/crates/precompile/src/blake2.rs @@ -1,5 +1,5 @@ use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use core::convert::TryInto; +use revm_primitives::Bytes; const F_ROUND: u64 = 1; const INPUT_LENGTH: usize = 213; @@ -10,7 +10,9 @@ pub const FUN: PrecompileWithAddress = /// reference: /// input format: /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] -fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { +fn run(input: &Bytes, gas_limit: u64) -> PrecompileResult { + let input = &input[..]; + if input.len() != INPUT_LENGTH { return Err(Error::Blake2WrongLength); } @@ -49,7 +51,7 @@ fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { out[i..i + 8].copy_from_slice(&h.to_le_bytes()); } - Ok((gas_used, out.to_vec())) + Ok((gas_used, out.into())) } mod algo { diff --git a/crates/precompile/src/bn128.rs b/crates/precompile/src/bn128.rs index 5e61d636ca..19b1f5f93e 100644 --- a/crates/precompile/src/bn128.rs +++ b/crates/precompile/src/bn128.rs @@ -1,8 +1,8 @@ use crate::{ utilities::right_pad, Address, Error, Precompile, PrecompileResult, PrecompileWithAddress, }; -use alloc::vec::Vec; use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; +use revm_primitives::Bytes; pub mod add { use super::*; @@ -11,8 +11,8 @@ pub mod add { pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - if 150 > target_gas { + Precompile::Standard(|input, gas_limit| { + if 150 > gas_limit { return Err(Error::OutOfGas); } Ok((150, super::run_add(input)?)) @@ -21,8 +21,8 @@ pub mod add { pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - if 500 > target_gas { + Precompile::Standard(|input, gas_limit| { + if 500 > gas_limit { return Err(Error::OutOfGas); } Ok((500, super::run_add(input)?)) @@ -37,7 +37,7 @@ pub mod mul { pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], gas_limit: u64| -> PrecompileResult { + Precompile::Standard(|input, gas_limit| { if 6_000 > gas_limit { return Err(Error::OutOfGas); } @@ -47,7 +47,7 @@ pub mod mul { pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], gas_limit: u64| -> PrecompileResult { + Precompile::Standard(|input, gas_limit| { if 40_000 > gas_limit { return Err(Error::OutOfGas); } @@ -65,12 +65,12 @@ pub mod pair { const ISTANBUL_PAIR_BASE: u64 = 45_000; pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { + Precompile::Standard(|input, gas_limit| { super::run_pair( input, ISTANBUL_PAIR_PER_POINT, ISTANBUL_PAIR_BASE, - target_gas, + gas_limit, ) }), ); @@ -79,12 +79,12 @@ pub mod pair { const BYZANTIUM_PAIR_BASE: u64 = 100_000; pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { + Precompile::Standard(|input, gas_limit| { super::run_pair( input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, - target_gas, + gas_limit, ) }), ); @@ -132,7 +132,7 @@ fn new_g1_point(px: Fq, py: Fq) -> Result { } } -fn run_add(input: &[u8]) -> Result, Error> { +fn run_add(input: &[u8]) -> Result { let input = right_pad::(input); let p1 = read_point(&input[..64])?; @@ -153,7 +153,7 @@ fn run_add(input: &[u8]) -> Result, Error> { Ok(output.into()) } -fn run_mul(input: &[u8]) -> Result, Error> { +fn run_mul(input: &[u8]) -> Result { let input = right_pad::(input); let p = read_point(&input[..64])?; @@ -166,7 +166,7 @@ fn run_mul(input: &[u8]) -> Result, Error> { mul.x().to_big_endian(&mut out[..32]).unwrap(); mul.y().to_big_endian(&mut out[32..]).unwrap(); } - Ok(out.to_vec()) + Ok(out.into()) } fn run_pair( @@ -225,7 +225,7 @@ fn run_pair( let mut out = [0u8; 32]; out[31] = success as u8; - Ok((gas_used, out.to_vec())) + Ok((gas_used, out.into())) } /* diff --git a/crates/precompile/src/hash.rs b/crates/precompile/src/hash.rs index c8ad6c9df1..b170cd1c51 100644 --- a/crates/precompile/src/hash.rs +++ b/crates/precompile/src/hash.rs @@ -1,5 +1,6 @@ use super::calc_linear_cost_u32; use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; +use revm_primitives::Bytes; use sha2::Digest; pub const SHA256: PrecompileWithAddress = @@ -13,20 +14,20 @@ pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress( /// See: /// See: /// See: -fn sha256_run(input: &[u8], gas_limit: u64) -> PrecompileResult { +fn sha256_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let cost = calc_linear_cost_u32(input.len(), 60, 12); if cost > gas_limit { Err(Error::OutOfGas) } else { - let output = sha2::Sha256::digest(input).to_vec(); - Ok((cost, output)) + let output = sha2::Sha256::digest(input); + Ok((cost, output.to_vec().into())) } } /// See: /// See: /// See: -fn ripemd160_run(input: &[u8], gas_limit: u64) -> PrecompileResult { +fn ripemd160_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), 600, 120); if gas_used > gas_limit { Err(Error::OutOfGas) @@ -36,6 +37,6 @@ fn ripemd160_run(input: &[u8], gas_limit: u64) -> PrecompileResult { let mut output = [0u8; 32]; hasher.finalize_into((&mut output[12..]).into()); - Ok((gas_used, output.to_vec())) + Ok((gas_used, output.to_vec().into())) } } diff --git a/crates/precompile/src/identity.rs b/crates/precompile/src/identity.rs index 8b743799ee..efe97e8a32 100644 --- a/crates/precompile/src/identity.rs +++ b/crates/precompile/src/identity.rs @@ -1,5 +1,6 @@ use super::calc_linear_cost_u32; use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; +use revm_primitives::Bytes; pub const FUN: PrecompileWithAddress = PrecompileWithAddress(crate::u64_to_address(4), Precompile::Standard(identity_run)); @@ -13,10 +14,10 @@ const IDENTITY_PER_WORD: u64 = 3; /// /// See: /// See: -fn identity_run(input: &[u8], gas_limit: u64) -> PrecompileResult { +fn identity_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), IDENTITY_BASE, IDENTITY_PER_WORD); if gas_used > gas_limit { return Err(Error::OutOfGas); } - Ok((gas_used, input.to_vec())) + Ok((gas_used, input.clone())) } diff --git a/crates/precompile/src/kzg_point_evaluation.rs b/crates/precompile/src/kzg_point_evaluation.rs index c1aedd0261..e4abf2dc35 100644 --- a/crates/precompile/src/kzg_point_evaluation.rs +++ b/crates/precompile/src/kzg_point_evaluation.rs @@ -1,6 +1,6 @@ use crate::{Address, Error, Precompile, PrecompileResult, PrecompileWithAddress}; use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; -use revm_primitives::{hex_literal::hex, Env}; +use revm_primitives::{hex_literal::hex, Bytes, Env}; use sha2::{Digest, Sha256}; // TODO: remove when we have `portable` feature in `c-kzg` @@ -27,7 +27,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: &[u8], gas_limit: u64, env: &Env) -> PrecompileResult { +fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult { if gas_limit < GAS_COST { return Err(Error::OutOfGas); } @@ -54,7 +54,7 @@ fn run(input: &[u8], gas_limit: u64, env: &Env) -> PrecompileResult { } // Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values - Ok((GAS_COST, RETURN_VALUE.to_vec())) + Ok((GAS_COST, RETURN_VALUE.into())) } /// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` @@ -116,8 +116,8 @@ mod tests { let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); let gas = 50000; let env = Env::default(); - let (actual_gas, actual_output) = run(&input, gas, &env).unwrap(); + let (actual_gas, actual_output) = run(&input.into(), gas, &env).unwrap(); assert_eq!(actual_gas, gas); - assert_eq!(actual_output, expected_output); + assert_eq!(actual_output[..], expected_output); } } diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index 3b47ab9577..903832fbed 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -1,11 +1,11 @@ use crate::{ primitives::U256, - utilities::{left_pad, left_pad_vec, right_pad_with_offset, right_pad_with_offset_vec}, + utilities::{left_pad, left_pad_vec, right_pad_vec, right_pad_with_offset}, Error, Precompile, PrecompileResult, PrecompileWithAddress, }; -use alloc::vec::Vec; use aurora_engine_modexp::modexp; use core::cmp::{max, min}; +use revm_primitives::Bytes; pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( crate::u64_to_address(5), @@ -17,13 +17,13 @@ pub const BERLIN: PrecompileWithAddress = /// See: /// See: -fn byzantium_run(input: &[u8], gas_limit: u64) -> PrecompileResult { +fn byzantium_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { run_inner(input, gas_limit, 0, |a, b, c, d| { byzantium_gas_calc(a, b, c, d) }) } -pub fn berlin_run(input: &[u8], gas_limit: u64) -> PrecompileResult { +pub fn berlin_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { run_inner(input, gas_limit, 200, |a, b, c, d| { berlin_gas_calc(a, b, c, d) }) @@ -52,6 +52,7 @@ where if min_gas > gas_limit { return Err(Error::OutOfGas); } + // The format of input is: // // Where every length is a 32-byte left-padded integer representing the number of bytes @@ -71,12 +72,12 @@ where return Err(Error::ModexpModOverflow); }; - // Handle a special case when both the base and mod length is zero + // Handle a special case when both the base and mod length are zero. if base_len == 0 && mod_len == 0 { - return Ok((min_gas, Vec::new())); + return Ok((min_gas, Bytes::new())); } - // cast exponent length to usize, it does not make sense to handle larger values. + // Cast exponent length to usize, since it does not make sense to handle larger values. let Ok(exp_len) = usize::try_from(exp_len) else { return Err(Error::ModexpModOverflow); }; @@ -84,13 +85,8 @@ where // Used to extract ADJUSTED_EXPONENT_LENGTH. let exp_highp_len = min(exp_len, 32); - // throw away the header data as we already extracted lengths. - let input = if input.len() >= 96 { - &input[HEADER_LENGTH..] - } else { - // or set input to zero if there is no more data - &[] - }; + // Throw away the header data as we already extracted lengths. + let input = input.get(HEADER_LENGTH..).unwrap_or_default(); let exp_highp = { // get right padded bytes so if data.len is less then exp_len we will get right padded zeroes. @@ -100,23 +96,24 @@ where U256::from_be_bytes(out.into_owned()) }; - // calculate gas spent. + // Check if we have enough gas. let gas_cost = calc_gas(base_len as u64, exp_len as u64, mod_len as u64, &exp_highp); - // check if we have enough gas. if gas_cost > gas_limit { return Err(Error::OutOfGas); } // Padding is needed if the input does not contain all 3 values. - let base = right_pad_with_offset_vec(input, 0, base_len); - let exponent = right_pad_with_offset_vec(input, base_len, exp_len); - let modulus = right_pad_with_offset_vec(input, base_len.saturating_add(exp_len), mod_len); + let input_len = base_len.saturating_add(exp_len).saturating_add(mod_len); + let input = right_pad_vec(input, input_len); + let (base, input) = input.split_at(base_len); + let (exponent, modulus) = input.split_at(exp_len); + debug_assert_eq!(modulus.len(), mod_len); // Call the modexp. - let output = modexp(&base, &exponent, &modulus); + let output = modexp(base, exponent, modulus); // left pad the result to modulus length. bytes will always by less or equal to modulus length. - Ok((gas_cost, left_pad_vec(&output, mod_len).into_owned())) + Ok((gas_cost, left_pad_vec(&output, mod_len).into_owned().into())) } fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { @@ -163,6 +160,7 @@ fn berlin_gas_calc(base_length: u64, exp_length: u64, mod_length: u64, exp_highp #[cfg(test)] mod tests { use super::*; + use alloc::vec::Vec; use revm_primitives::hex; struct Test { @@ -343,8 +341,7 @@ mod tests { #[test] fn test_byzantium_modexp_gas() { for (test, &test_gas) in TESTS.iter().zip(BYZANTIUM_GAS.iter()) { - let input = hex::decode(test.input).unwrap(); - + let input = hex::decode(test.input).unwrap().into(); let res = byzantium_run(&input, 100_000_000).unwrap(); let expected = hex::decode(test.expected).unwrap(); assert_eq!( @@ -359,7 +356,7 @@ mod tests { #[test] fn test_berlin_modexp_gas() { for (test, &test_gas) in TESTS.iter().zip(BERLIN_GAS.iter()) { - let input = hex::decode(test.input).unwrap(); + let input = hex::decode(test.input).unwrap().into(); let res = berlin_run(&input, 100_000_000).unwrap(); let expected = hex::decode(test.expected).unwrap(); assert_eq!( @@ -373,7 +370,7 @@ mod tests { #[test] fn test_berlin_modexp_empty_input() { - let res = berlin_run(&[], 100_000).unwrap(); + let res = berlin_run(&Bytes::new(), 100_000).unwrap(); let expected: Vec = Vec::new(); assert_eq!(res.1, expected) } diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index e5bfb0cb05..352d7024c5 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -1,6 +1,5 @@ use crate::{utilities::right_pad, Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use alloc::vec::Vec; -use revm_primitives::{alloy_primitives::B512, B256}; +use revm_primitives::{alloy_primitives::B512, Bytes, B256}; pub const ECRECOVER: PrecompileWithAddress = PrecompileWithAddress( crate::u64_to_address(1), @@ -65,10 +64,10 @@ mod secp256k1 { } } -fn ec_recover_run(input: &[u8], target_gas: u64) -> PrecompileResult { +fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { const ECRECOVER_BASE: u64 = 3_000; - if ECRECOVER_BASE > target_gas { + if ECRECOVER_BASE > gas_limit { return Err(Error::OutOfGas); } @@ -76,7 +75,7 @@ fn ec_recover_run(input: &[u8], target_gas: u64) -> PrecompileResult { // `v` must be a 32-byte big-endian integer equal to 27 or 28. if !(input[32..63].iter().all(|&b| b == 0) && matches!(input[63], 27 | 28)) { - return Ok((ECRECOVER_BASE, Vec::new())); + return Ok((ECRECOVER_BASE, Bytes::new())); } let msg = <&B256>::try_from(&input[0..32]).unwrap(); @@ -84,8 +83,7 @@ fn ec_recover_run(input: &[u8], target_gas: u64) -> PrecompileResult { let sig = <&B512>::try_from(&input[64..128]).unwrap(); let out = secp256k1::ecrecover(sig, recid, msg) - .map(|o| o.to_vec()) + .map(|o| o.to_vec().into()) .unwrap_or_default(); - Ok((ECRECOVER_BASE, out)) } diff --git a/crates/precompile/src/utilities.rs b/crates/precompile/src/utilities.rs index b62f8dfc13..22ed7f7d0b 100644 --- a/crates/precompile/src/utilities.rs +++ b/crates/precompile/src/utilities.rs @@ -1,5 +1,4 @@ use alloc::borrow::Cow; -use core::cmp::min; /// Right-pads the given slice at `offset` with zeroes until `LEN`. /// @@ -26,8 +25,7 @@ pub fn right_pad(data: &[u8]) -> Cow<'_, [u8; LEN]> { Cow::Borrowed(data.try_into().unwrap()) } else { let mut padded = [0; LEN]; - let end = min(LEN, data.len()); - padded[..end].copy_from_slice(&data[..end]); + padded[..data.len()].copy_from_slice(data); Cow::Owned(padded) } } @@ -41,8 +39,7 @@ pub fn right_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> { Cow::Borrowed(data) } else { let mut padded = vec![0; len]; - let end = min(len, data.len()); - padded[..end].copy_from_slice(&data[..end]); + padded[..data.len()].copy_from_slice(data); Cow::Owned(padded) } } @@ -56,8 +53,7 @@ pub fn left_pad(data: &[u8]) -> Cow<'_, [u8; LEN]> { Cow::Borrowed(data.try_into().unwrap()) } else { let mut padded = [0; LEN]; - let end = min(LEN, data.len()); - padded[LEN - end..].copy_from_slice(&data[..end]); + padded[LEN - data.len()..].copy_from_slice(data); Cow::Owned(padded) } } @@ -71,8 +67,7 @@ pub fn left_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> { Cow::Borrowed(data) } else { let mut padded = vec![0; len]; - let end = min(len, data.len()); - padded[len - end..].copy_from_slice(&data[..end]); + padded[len - data.len()..].copy_from_slice(data); Cow::Owned(padded) } } diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index 583ce8c11f..c8dba6ebb4 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -1,14 +1,14 @@ use crate::Env; -use alloc::vec::Vec; +use alloy_primitives::Bytes; use core::fmt; /// A precompile operation result. /// /// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`. -pub type PrecompileResult = Result<(u64, Vec), PrecompileError>; +pub type PrecompileResult = Result<(u64, Bytes), PrecompileError>; -pub type StandardPrecompileFn = fn(&[u8], u64) -> PrecompileResult; -pub type EnvPrecompileFn = fn(&[u8], u64, env: &Env) -> PrecompileResult; +pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; +pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum PrecompileError { diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 0fe1161175..1646bc35c1 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -130,6 +130,7 @@ impl EvmContext { l1_block_info: self.l1_block_info, } } + pub fn new(db: DB) -> Self { Self { env: Box::default(), @@ -444,7 +445,6 @@ impl EvmContext { gas: Gas, ) -> InterpreterResult { let input_data = &inputs.input; - let out = match precompile { Precompile::Standard(fun) => fun(input_data, gas.limit()), Precompile::Env(fun) => fun(input_data, gas.limit(), self.env()), @@ -460,13 +460,13 @@ impl EvmContext { Ok((gas_used, data)) => { if result.gas.record_cost(gas_used) { result.result = InstructionResult::Return; - result.output = Bytes::from(data); + result.output = data; } else { result.result = InstructionResult::PrecompileOOG; } } Err(e) => { - result.result = if crate::precompile::Error::OutOfGas == e { + result.result = if e == crate::precompile::Error::OutOfGas { InstructionResult::PrecompileOOG } else { InstructionResult::PrecompileError