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): don't allocate if padding is not needed #1075

Merged
merged 2 commits into from
Feb 12, 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
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
22 changes: 11 additions & 11 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::{left_pad, left_pad_vec, right_pad_with_offset, right_pad_with_offset_vec},
Error, Precompile, PrecompileResult, PrecompileWithAddress,
};
use alloc::vec::Vec;
Expand Down Expand Up @@ -59,9 +59,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(right_pad_with_offset::<32>(input, 0).into_owned());
let exp_len = U256::from_be_bytes(right_pad_with_offset::<32>(input, 32).into_owned());
let mod_len = U256::from_be_bytes(right_pad_with_offset::<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 @@ -94,10 +94,10 @@ where

let exp_highp = {
// 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);
let right_padded_highp = right_pad_with_offset::<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 @@ -108,15 +108,15 @@ where
}

// Padding is needed if the input does not contain all 3 values.
let base = get_right_padded_vec(input, 0, base_len);
let exponent = get_right_padded_vec(input, base_len, exp_len);
let modulus = get_right_padded_vec(input, base_len.saturating_add(exp_len), mod_len);
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);

// 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_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 right_pad_with_offset<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 right_pad_with_offset_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 = right_pad_with_offset::<8>(&data, 4);
assert!(matches!(padded, Cow::Owned(_)));
assert_eq!(padded[..], [0, 0, 0, 0, 0, 0, 0, 0]);
let padded = right_pad_with_offset_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 = right_pad_with_offset::<8>(&data, 0);
assert!(matches!(padded, Cow::Borrowed(_)));
assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
let padded = right_pad_with_offset_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 = right_pad_with_offset::<8>(&data, 4);
assert!(matches!(padded, Cow::Owned(_)));
assert_eq!(padded[..], [5, 6, 7, 8, 0, 0, 0, 0]);
let padded = right_pad_with_offset_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]);
}
}
Loading