Skip to content

Commit

Permalink
perf(precompile): don't allocate if padding is not needed
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Feb 10, 2024
1 parent 08ae23f commit f2d68e6
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 44 deletions.
7 changes: 3 additions & 4 deletions crates/precompile/src/bn128.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -134,7 +133,7 @@ fn new_g1_point(px: Fq, py: Fq) -> Result<G1, Error> {
}

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

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

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

let p = read_point(&input[..64])?;

Expand Down
14 changes: 7 additions & 7 deletions crates/precompile/src/modexp.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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 {
Expand Down
6 changes: 2 additions & 4 deletions crates/precompile/src/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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)) {
Expand Down
164 changes: 135 additions & 29 deletions crates/precompile/src/utilities.rs
Original file line number Diff line number Diff line change
@@ -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<const LEN: usize>(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<const LEN: usize>(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<u8> {
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<const LEN: usize>(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<const LEN: usize>(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<u8> {
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<const LEN: usize>(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]);
}
}

0 comments on commit f2d68e6

Please sign in to comment.