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

refactor(precompile): make use of padding utilities, simplify secp256k1 #1073

Merged
merged 2 commits into from
Feb 11, 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
8 changes: 3 additions & 5 deletions crates/precompile/src/blake2.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress, StandardPrecompileFn};
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress};
use core::convert::TryInto;

const F_ROUND: u64 = 1;
const INPUT_LENGTH: usize = 213;

pub const FUN: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(9),
Precompile::Standard(run as StandardPrecompileFn),
);
pub const FUN: PrecompileWithAddress =
PrecompileWithAddress(crate::u64_to_address(9), Precompile::Standard(run));

/// reference: <https://eips.ethereum.org/EIPS/eip-152>
/// input format:
Expand Down
22 changes: 12 additions & 10 deletions crates/precompile/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use super::calc_linear_cost_u32;
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress, StandardPrecompileFn};
use sha2::*;
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress};
use sha2::Digest;

pub const SHA256: PrecompileWithAddress =
PrecompileWithAddress(crate::u64_to_address(2), Precompile::Standard(sha256_run));

pub const SHA256: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(2),
Precompile::Standard(sha256_run as StandardPrecompileFn),
);
pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(3),
Precompile::Standard(ripemd160_run as StandardPrecompileFn),
Precompile::Standard(ripemd160_run),
);

/// See: <https://ethereum.github.io/yellowpaper/paper.pdf>
Expand All @@ -32,8 +31,11 @@ fn ripemd160_run(input: &[u8], gas_limit: u64) -> PrecompileResult {
if gas_used > gas_limit {
Err(Error::OutOfGas)
} else {
let mut ret = [0u8; 32];
ret[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
Ok((gas_used, ret.to_vec()))
let mut hasher = ripemd::Ripemd160::new();
hasher.update(input);

let mut output = [0u8; 32];
hasher.finalize_into((&mut output[12..]).into());
Ok((gas_used, output.to_vec()))
Copy link
Member

Choose a reason for hiding this comment

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

The previous version seems better

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

::digest returns a new array so this avoids the copy_from_slice by directly using the output buffer

}
}
8 changes: 3 additions & 5 deletions crates/precompile/src/identity.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use super::calc_linear_cost_u32;
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress, StandardPrecompileFn};
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress};

pub const FUN: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(4),
Precompile::Standard(identity_run as StandardPrecompileFn),
);
pub const FUN: PrecompileWithAddress =
PrecompileWithAddress(crate::u64_to_address(4), Precompile::Standard(identity_run));

/// The base cost of the operation.
const IDENTITY_BASE: u64 = 15;
Expand Down
4 changes: 2 additions & 2 deletions crates/precompile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ impl Precompiles {

/// Returns the precompile for the given address.
#[inline]
pub fn get(&self, address: &Address) -> Option<Precompile> {
self.inner.get(address).cloned()
pub fn get(&self, address: &Address) -> Option<&Precompile> {
Copy link
Member

Choose a reason for hiding this comment

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

Precompile is just a function, as we know concrete type, it is okay to clone it

self.inner.get(address)
}

/// Is the precompiles list empty.
Expand Down
24 changes: 6 additions & 18 deletions crates/precompile/src/modexp.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use crate::{
primitives::U256,
utilities::{get_right_padded, get_right_padded_vec, left_padding, left_padding_vec},
Error, Precompile, PrecompileResult, PrecompileWithAddress, StandardPrecompileFn,
Error, Precompile, PrecompileResult, PrecompileWithAddress,
};
use alloc::vec::Vec;
use aurora_engine_modexp::modexp;
use core::cmp::{max, min};

pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(5),
Precompile::Standard(byzantium_run as StandardPrecompileFn),
Precompile::Standard(byzantium_run),
);

pub const BERLIN: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(5),
Precompile::Standard(berlin_run as StandardPrecompileFn),
);
pub const BERLIN: PrecompileWithAddress =
PrecompileWithAddress(crate::u64_to_address(5), Precompile::Standard(berlin_run));

/// See: <https://eips.ethereum.org/EIPS/eip-198>
/// See: <https://etherscan.io/address/0000000000000000000000000000000000000005>
Expand Down Expand Up @@ -139,12 +137,7 @@ fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U25
let iter_count = U256::from(calculate_iteration_count(exp_len, exp_highp));
// mul * iter_count bounded by 2^195 < 2^256 (no overflow)
let gas = (mul * iter_count) / U256::from(20);

if gas.as_limbs()[1] != 0 || gas.as_limbs()[2] != 0 || gas.as_limbs()[3] != 0 {
u64::MAX
} else {
gas.as_limbs()[0]
}
gas.saturating_to()
Copy link
Member

Choose a reason for hiding this comment

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

Liking this a lot!

}

// Calculate gas cost according to EIP 2565:
Expand All @@ -163,12 +156,7 @@ fn berlin_gas_calc(base_length: u64, exp_length: u64, mod_length: u64, exp_highp
let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length);
let iteration_count = calculate_iteration_count(exp_length, exp_highp);
let gas = (multiplication_complexity * U256::from(iteration_count)) / U256::from(3);

if gas.as_limbs()[1] != 0 || gas.as_limbs()[2] != 0 || gas.as_limbs()[3] != 0 {
u64::MAX
} else {
max(200, gas.as_limbs()[0])
}
max(200, gas.saturating_to())
}

#[cfg(test)]
Expand Down
51 changes: 24 additions & 27 deletions crates/precompile/src/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress, StandardPrecompileFn};
use crate::{
utilities::get_right_padded, Error, Precompile, PrecompileResult, PrecompileWithAddress,
};
use alloc::vec::Vec;
use core::cmp::min;
use revm_primitives::B256;
use revm_primitives::{alloy_primitives::B512, B256};

pub const ECRECOVER: PrecompileWithAddress = PrecompileWithAddress(
crate::u64_to_address(1),
Precompile::Standard(ec_recover_run as StandardPrecompileFn),
Precompile::Standard(ec_recover_run),
);

#[cfg(not(feature = "secp256k1"))]
#[allow(clippy::module_inception)]
mod secp256k1 {
use crate::B256;
use k256::ecdsa::{Error, RecoveryId, Signature, VerifyingKey};
use revm_primitives::keccak256;
use revm_primitives::{alloy_primitives::B512, keccak256, B256};

pub fn ecrecover(sig: &[u8; 65], msg: &B256) -> Result<B256, Error> {
pub fn ecrecover(sig: &B512, mut recid: u8, msg: &B256) -> Result<B256, Error> {
// parse signature
let mut recid = sig[64];
let mut sig = Signature::from_slice(&sig[..64])?;
let mut sig = Signature::from_slice(sig.as_slice())?;

// normalize signature and flip recovery id if needed.
if let Some(sig_normalized) = sig.normalize_s() {
sig = sig_normalized;
recid = recid ^ 1;
};
let recid = RecoveryId::from_byte(recid).expect("Recovery id is valid");
}
let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");

// recover key
let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)?;
Expand All @@ -45,8 +44,7 @@ mod secp256k1 {
#[cfg(feature = "secp256k1")]
#[allow(clippy::module_inception)]
mod secp256k1 {
use crate::B256;
use revm_primitives::keccak256;
use revm_primitives::{alloy_primitives::B512, keccak256, B256};
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message, Secp256k1,
Expand All @@ -55,40 +53,39 @@ mod secp256k1 {
// Silence the unused crate dependency warning.
use k256 as _;

pub fn ecrecover(sig: &[u8; 65], msg: &B256) -> Result<B256, secp256k1::Error> {
let sig =
RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?;
pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result<B256, secp256k1::Error> {
let recid = RecoveryId::from_i32(recid as i32).expect("recovery ID is valid");
let sig = RecoverableSignature::from_compact(sig.as_slice(), recid)?;

let secp = Secp256k1::new();
let public = secp.recover_ecdsa(&Message::from_digest_slice(&msg[..])?, &sig)?;
let msg = Message::from_digest_slice(msg.as_slice())?;
let public = secp.recover_ecdsa(&msg, &sig)?;

let mut hash = keccak256(&public.serialize_uncompressed()[1..]);
hash[..12].fill(0);
Ok(hash)
}
}

fn ec_recover_run(i: &[u8], target_gas: u64) -> PrecompileResult {
fn ec_recover_run(input: &[u8], target_gas: u64) -> PrecompileResult {
const ECRECOVER_BASE: u64 = 3_000;

if ECRECOVER_BASE > target_gas {
return Err(Error::OutOfGas);
}
let mut input = [0u8; 128];
input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]);

let msg = B256::from_slice(&input[0..32]);
let input = get_right_padded::<128>(input, 0);

let mut sig = [0u8; 65];
sig[0..64].copy_from_slice(&input[64..128]);

if input[32..63] != [0u8; 31] || !matches!(input[63], 27 | 28) {
// `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()));
}

sig[64] = input[63] - 27;
let msg = <&B256>::try_from(&input[0..32]).unwrap();
let recid = input[63] - 27;
let sig = <&B512>::try_from(&input[64..128]).unwrap();

let out = secp256k1::ecrecover(&sig, &msg)
let out = secp256k1::ecrecover(sig, recid, msg)
.map(|o| o.to_vec())
.unwrap_or_default();

Expand Down
25 changes: 12 additions & 13 deletions crates/precompile/src/utilities.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use core::cmp::min;

use alloc::vec::Vec;
use core::cmp::min;

/// Get an array from the data, if data does not contain `start` to `len` bytes, add right padding with
/// zeroes
/// Get an array from the data, if data does not contain `start` to `len` bytes,
/// add right padding with zeroes.
#[inline(always)]
pub fn get_right_padded<const S: usize>(data: &[u8], offset: usize) -> [u8; S] {
let mut padded = [0; S];
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(S), data.len());
let end = min(start.saturating_add(LEN), data.len());
padded[..end - start].copy_from_slice(&data[start..end]);
padded
}

/// Get a vector of the data, if data does not contain the slice of `start` to `len`, right pad missing
/// part with zeroes
/// Get a vector of the data, if data does not contain the slice of `start` to `len`,
/// right pad missing part with zeroes.
#[inline(always)]
pub fn get_right_padded_vec(data: &[u8], offset: usize, len: usize) -> Vec<u8> {
let mut padded = vec![0; len];
Expand All @@ -26,10 +25,10 @@ pub fn get_right_padded_vec(data: &[u8], offset: usize, len: usize) -> Vec<u8> {

/// Left padding until `len`. If data is more then len, truncate the right most bytes.
#[inline(always)]
pub fn left_padding<const S: usize>(data: &[u8]) -> [u8; S] {
let mut padded = [0; S];
let end = min(S, data.len());
padded[S - end..].copy_from_slice(&data[..end]);
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
}

Expand Down
4 changes: 2 additions & 2 deletions crates/revm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl<DB: Database> EvmContext<DB> {
/// Sets precompiles
pub fn set_precompiles(&mut self, precompiles: Precompiles) {
self.journaled_state.warm_preloaded_addresses =
precompiles.addresses().cloned().collect::<HashSet<_>>();
precompiles.addresses().copied().collect::<HashSet<_>>();
self.precompiles = precompiles;
}

Expand Down Expand Up @@ -415,7 +415,7 @@ impl<DB: Database> EvmContext<DB> {
}

if let Some(precompile) = self.precompiles.get(&inputs.contract) {
let result = self.call_precompile(precompile, inputs, gas);
let result = self.call_precompile(precompile.clone(), inputs, gas);
if matches!(result.result, return_ok!()) {
self.journaled_state.checkpoint_commit();
} else {
Expand Down
Loading