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

perf(precompile): use Bytes in precompile functions #1085

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions crates/interpreter/src/instructions/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ macro_rules! as_u64_saturated {

macro_rules! as_usize_saturated {
($v:expr) => {
::core::convert::TryInto::<usize>::try_into(as_u64_saturated!($v)).unwrap_or(usize::MAX)
usize::try_from(as_u64_saturated!($v)).unwrap_or(usize::MAX)
};
}

Expand All @@ -234,7 +234,7 @@ macro_rules! as_usize_or_fail_ret {
$interp.instruction_result = $reason;
return $ret;
}
let Ok(val) = ::core::convert::TryInto::<usize>::try_into(x[0]) else {
let Ok(val) = usize::try_from(x[0]) else {
$interp.instruction_result = $reason;
return $ret;
};
Expand Down
8 changes: 5 additions & 3 deletions crates/precompile/src/blake2.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,7 +10,9 @@ pub const FUN: PrecompileWithAddress =
/// reference: <https://eips.ethereum.org/EIPS/eip-152>
/// 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);
}
Expand Down Expand Up @@ -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 {
Expand Down
30 changes: 15 additions & 15 deletions crates/precompile/src/bn128.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -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)?))
Expand All @@ -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)?))
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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,
)
}),
);
Expand All @@ -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,
)
}),
);
Expand Down Expand Up @@ -132,7 +132,7 @@ fn new_g1_point(px: Fq, py: Fq) -> Result<G1, Error> {
}
}

fn run_add(input: &[u8]) -> Result<Vec<u8>, Error> {
fn run_add(input: &[u8]) -> Result<Bytes, Error> {
let input = right_pad::<ADD_INPUT_LEN>(input);

let p1 = read_point(&input[..64])?;
Expand All @@ -153,7 +153,7 @@ fn run_add(input: &[u8]) -> Result<Vec<u8>, Error> {
Ok(output.into())
}

fn run_mul(input: &[u8]) -> Result<Vec<u8>, Error> {
fn run_mul(input: &[u8]) -> Result<Bytes, Error> {
let input = right_pad::<MUL_INPUT_LEN>(input);

let p = read_point(&input[..64])?;
Expand All @@ -166,7 +166,7 @@ fn run_mul(input: &[u8]) -> Result<Vec<u8>, 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(
Expand Down Expand Up @@ -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()))
}

/*
Expand Down
11 changes: 6 additions & 5 deletions crates/precompile/src/hash.rs
Original file line number Diff line number Diff line change
@@ -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 =
Expand All @@ -13,20 +14,20 @@ pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress(
/// See: <https://ethereum.github.io/yellowpaper/paper.pdf>
/// See: <https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions>
/// See: <https://etherscan.io/address/0000000000000000000000000000000000000002>
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: <https://ethereum.github.io/yellowpaper/paper.pdf>
/// See: <https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions>
/// See: <https://etherscan.io/address/0000000000000000000000000000000000000003>
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)
Expand All @@ -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()))
}
}
5 changes: 3 additions & 2 deletions crates/precompile/src/identity.rs
Original file line number Diff line number Diff line change
@@ -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));
Expand All @@ -13,10 +14,10 @@ const IDENTITY_PER_WORD: u64 = 3;
///
/// See: <https://ethereum.github.io/yellowpaper/paper.pdf>
/// See: <https://etherscan.io/address/0000000000000000000000000000000000000004>
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()))
Copy link
Collaborator Author

@DaniPopes DaniPopes Feb 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the biggest win IMO, avoids malloc+memcpy basically as if we're using Arc. identity precompile is common in code generated by Vyper.

}
10 changes: 5 additions & 5 deletions crates/precompile/src/kzg_point_evaluation.rs
Original file line number Diff line number Diff line change
@@ -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`
Expand All @@ -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);
}
Expand All @@ -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..]`
Expand Down Expand Up @@ -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);
}
}
44 changes: 20 additions & 24 deletions crates/precompile/src/modexp.rs
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -17,13 +17,13 @@ pub const BERLIN: PrecompileWithAddress =

/// See: <https://eips.ethereum.org/EIPS/eip-198>
/// See: <https://etherscan.io/address/0000000000000000000000000000000000000005>
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)
})
Expand Down Expand Up @@ -52,6 +52,7 @@ where
if min_gas > gas_limit {
return Err(Error::OutOfGas);
}

// The format of input is:
// <length_of_BASE> <length_of_EXPONENT> <length_of_MODULUS> <BASE> <EXPONENT> <MODULUS>
// Where every length is a 32-byte left-padded integer representing the number of bytes
Expand All @@ -71,26 +72,21 @@ 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);
};

// 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.
Expand All @@ -100,23 +96,23 @@ 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 = right_pad_vec(input, base_len + exp_len + mod_len);
let (base, input) = input.split_at(base_len);
let (exponent, modulus) = input.split_at(exp_len);
debug_assert_eq!(modulus.len(), mod_len);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this allocate only once in case input is too short.

modulus.len() must be mod_len due to the padding above, this is the same as slicing the input 3 times.


// Call the modexp.
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 {
Expand Down Expand Up @@ -163,6 +159,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 {
Expand Down Expand Up @@ -343,8 +340,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!(
Expand All @@ -359,7 +355,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!(
Expand All @@ -373,7 +369,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<u8> = Vec::new();
assert_eq!(res.1, expected)
}
Expand Down
Loading
Loading