From f2d68e6a5ec4a69d3ccd0475b67db8710c1eda55 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 10 Feb 2024 23:42:27 +0100 Subject: [PATCH] perf(precompile): don't allocate if padding is not needed --- crates/precompile/src/bn128.rs | 7 +- crates/precompile/src/modexp.rs | 14 +-- crates/precompile/src/secp256k1.rs | 6 +- crates/precompile/src/utilities.rs | 164 ++++++++++++++++++++++++----- 4 files changed, 147 insertions(+), 44 deletions(-) diff --git a/crates/precompile/src/bn128.rs b/crates/precompile/src/bn128.rs index ac7c85ddc3..5e61d636ca 100644 --- a/crates/precompile/src/bn128.rs +++ b/crates/precompile/src/bn128.rs @@ -1,6 +1,5 @@ use crate::{ - utilities::get_right_padded, Address, Error, Precompile, PrecompileResult, - PrecompileWithAddress, + utilities::right_pad, Address, Error, Precompile, PrecompileResult, PrecompileWithAddress, }; use alloc::vec::Vec; use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; @@ -134,7 +133,7 @@ fn new_g1_point(px: Fq, py: Fq) -> Result { } fn run_add(input: &[u8]) -> Result, Error> { - let input = get_right_padded::(input, 0); + let input = right_pad::(input); let p1 = read_point(&input[..64])?; let p2 = read_point(&input[64..])?; @@ -155,7 +154,7 @@ fn run_add(input: &[u8]) -> Result, Error> { } fn run_mul(input: &[u8]) -> Result, Error> { - let input = get_right_padded::(input, 0); + let input = right_pad::(input); let p = read_point(&input[..64])?; diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index c75e7213ac..32b03dd322 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -1,6 +1,6 @@ use crate::{ primitives::U256, - utilities::{get_right_padded, get_right_padded_vec, left_padding, left_padding_vec}, + utilities::{get_right_padded, get_right_padded_vec, left_pad, left_pad_vec}, Error, Precompile, PrecompileResult, PrecompileWithAddress, }; use alloc::vec::Vec; @@ -58,9 +58,9 @@ where const HEADER_LENGTH: usize = 96; // Extract the header. - let base_len = U256::from_be_bytes(get_right_padded::<32>(input, 0)); - let exp_len = U256::from_be_bytes(get_right_padded::<32>(input, 32)); - let mod_len = U256::from_be_bytes(get_right_padded::<32>(input, 64)); + let base_len = U256::from_be_bytes(get_right_padded::<32>(input, 0).into_owned()); + let exp_len = U256::from_be_bytes(get_right_padded::<32>(input, 32).into_owned()); + let mod_len = U256::from_be_bytes(get_right_padded::<32>(input, 64).into_owned()); // cast base and modulus to usize, it does not make sense to handle larger values let Ok(base_len) = usize::try_from(base_len) else { @@ -95,8 +95,8 @@ where // get right padded bytes so if data.len is less then exp_len we will get right padded zeroes. let right_padded_highp = get_right_padded::<32>(input, base_len); // If exp_len is less then 32 bytes get only exp_len bytes and do left padding. - let out = left_padding::<32>(&right_padded_highp[..exp_highp_len]); - U256::from_be_bytes(out) + let out = left_pad::<32>(&right_padded_highp[..exp_highp_len]); + U256::from_be_bytes(out.into_owned()) }; // calculate gas spent. @@ -115,7 +115,7 @@ where 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_padding_vec(&output, mod_len))) + Ok((gas_cost, left_pad_vec(&output, mod_len).into_owned())) } fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 48d528a22c..e5bfb0cb05 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -1,6 +1,4 @@ -use crate::{ - utilities::get_right_padded, Error, Precompile, PrecompileResult, PrecompileWithAddress, -}; +use crate::{utilities::right_pad, Error, Precompile, PrecompileResult, PrecompileWithAddress}; use alloc::vec::Vec; use revm_primitives::{alloy_primitives::B512, B256}; @@ -74,7 +72,7 @@ fn ec_recover_run(input: &[u8], target_gas: u64) -> PrecompileResult { return Err(Error::OutOfGas); } - let input = get_right_padded::<128>(input, 0); + let input = right_pad::<128>(input); // `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)) { diff --git a/crates/precompile/src/utilities.rs b/crates/precompile/src/utilities.rs index 5d63c9b33a..238f9cee19 100644 --- a/crates/precompile/src/utilities.rs +++ b/crates/precompile/src/utilities.rs @@ -1,42 +1,148 @@ -use alloc::vec::Vec; +use alloc::borrow::Cow; use core::cmp::min; -/// Get an array from the data, if data does not contain `start` to `len` bytes, -/// add right padding with zeroes. +/// Right-pads the given slice at `offset` with zeroes until `LEN`. +/// +/// Returns the first `LEN` bytes if it does not need padding. #[inline(always)] -pub fn get_right_padded(data: &[u8], offset: usize) -> [u8; LEN] { - let mut padded = [0; LEN]; - let start = min(offset, data.len()); - let end = min(start.saturating_add(LEN), data.len()); - padded[..end - start].copy_from_slice(&data[start..end]); - padded +pub fn get_right_padded(data: &[u8], offset: usize) -> Cow<'_, [u8; LEN]> { + right_pad(data.get(offset..).unwrap_or_default()) } -/// Get a vector of the data, if data does not contain the slice of `start` to `len`, -/// right pad missing part with zeroes. +/// Right-pads the given slice at `offset` with zeroes until `len`. +/// +/// Returns the first `len` bytes if it does not need padding. #[inline(always)] -pub fn get_right_padded_vec(data: &[u8], offset: usize, len: usize) -> Vec { - let mut padded = vec![0; len]; - let start = min(offset, data.len()); - let end = min(start.saturating_add(len), data.len()); - padded[..end - start].copy_from_slice(&data[start..end]); - padded +pub fn get_right_padded_vec(data: &[u8], offset: usize, len: usize) -> Cow<'_, [u8]> { + right_pad_vec(data.get(offset..).unwrap_or_default(), len) } -/// Left padding until `len`. If data is more then len, truncate the right most bytes. +/// Right-pads the given slice with zeroes until `LEN`. +/// +/// Returns the first `LEN` bytes if it does not need padding. #[inline(always)] -pub fn left_padding(data: &[u8]) -> [u8; LEN] { - let mut padded = [0; LEN]; - let end = min(LEN, data.len()); - padded[LEN - end..].copy_from_slice(&data[..end]); - padded +pub fn right_pad(data: &[u8]) -> Cow<'_, [u8; LEN]> { + if let Some(data) = data.get(..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]); + Cow::Owned(padded) + } } -/// Left padding until `len`. If data is more then len, truncate the right most bytes. +/// Right-pads the given slice with zeroes until `len`. +/// +/// Returns the first `len` bytes if it does not need padding. #[inline(always)] -pub fn left_padding_vec(data: &[u8], len: usize) -> Vec { - let mut padded = vec![0; len]; - let end = min(len, data.len()); - padded[len - end..].copy_from_slice(&data[..end]); - padded +pub fn right_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> { + if let Some(data) = data.get(..len) { + Cow::Borrowed(data) + } else { + let mut padded = vec![0; len]; + let end = min(len, data.len()); + padded[..end].copy_from_slice(&data[..end]); + Cow::Owned(padded) + } +} + +/// Left-pads the given slice with zeroes until `LEN`. +/// +/// Returns the first `LEN` bytes if it does not need padding. +#[inline(always)] +pub fn left_pad(data: &[u8]) -> Cow<'_, [u8; LEN]> { + if let Some(data) = data.get(..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]); + Cow::Owned(padded) + } +} + +/// Left-pads the given slice with zeroes until `len`. +/// +/// Returns the first `len` bytes if it does not need padding. +#[inline(always)] +pub fn left_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> { + if let Some(data) = data.get(..len) { + Cow::Borrowed(data) + } else { + let mut padded = vec![0; len]; + let end = min(len, data.len()); + padded[len - end..].copy_from_slice(&data[..end]); + Cow::Owned(padded) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_with_right_padding() { + let data = [1, 2, 3, 4]; + let padded = get_right_padded::<8>(&data, 4); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [0, 0, 0, 0, 0, 0, 0, 0]); + let padded = get_right_padded_vec(&data, 4, 8); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [0, 0, 0, 0, 0, 0, 0, 0]); + + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let padded = get_right_padded::<8>(&data, 0); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + let padded = get_right_padded_vec(&data, 0, 8); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let padded = get_right_padded::<8>(&data, 4); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [5, 6, 7, 8, 0, 0, 0, 0]); + let padded = get_right_padded_vec(&data, 4, 8); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [5, 6, 7, 8, 0, 0, 0, 0]); + } + + #[test] + fn right_padding() { + let data = [1, 2, 3, 4]; + let padded = right_pad::<8>(&data); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 0, 0, 0, 0]); + let padded = right_pad_vec(&data, 8); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 0, 0, 0, 0]); + + let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let padded = right_pad::<8>(&data); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + let padded = right_pad_vec(&data, 8); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + } + + #[test] + fn left_padding() { + let data = [1, 2, 3, 4]; + let padded = left_pad::<8>(&data); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [0, 0, 0, 0, 1, 2, 3, 4]); + let padded = left_pad_vec(&data, 8); + assert!(matches!(padded, Cow::Owned(_))); + assert_eq!(padded[..], [0, 0, 0, 0, 1, 2, 3, 4]); + + let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let padded = left_pad::<8>(&data); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + let padded = left_pad_vec(&data, 8); + assert!(matches!(padded, Cow::Borrowed(_))); + assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]); + } }