Skip to content
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
1 change: 1 addition & 0 deletions crates/common/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub use bytes::Bytes;
pub mod base64;
pub use ethrex_trie::{TrieLogger, TrieWitness};
pub mod tracing;
pub mod utils;
35 changes: 35 additions & 0 deletions crates/common/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use ethereum_types::U256;

/// Converts a big endian slice to a u256, faster than `u256::from_big_endian`.
pub fn u256_from_big_endian(slice: &[u8]) -> U256 {
let mut padded = [0u8; 32];
padded[32 - slice.len()..32].copy_from_slice(slice);

let mut ret = [0; 4];

let mut u64_bytes = [0u8; 8];
for i in 0..4 {
u64_bytes.copy_from_slice(&padded[8 * i..(8 * i + 8)]);
ret[4 - i - 1] = u64::from_be_bytes(u64_bytes);
}

U256(ret)
}

/// Converts a constant big endian slice to a u256, faster than `u256::from_big_endian` and `u256_from_big_endian`.
///
/// Note: N should not exceed 32.
Comment thread
edg-l marked this conversation as resolved.
pub fn u256_from_big_endian_const<const N: usize>(slice: [u8; N]) -> U256 {
let mut padded = [0u8; 32];
padded[32 - N..32].copy_from_slice(&slice);

let mut ret = [0u64; 4];

let mut u64_bytes = [0u8; 8];
for i in 0..4 {
u64_bytes.copy_from_slice(&padded[8 * i..(8 * i + 8)]);
ret[4 - i - 1] = u64::from_be_bytes(u64_bytes);
}

U256(ret)
}
9 changes: 7 additions & 2 deletions crates/vm/levm/src/execution_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,13 @@ impl<'a> VM<'a> {
Opcode::BLOBHASH => self.op_blobhash(),
Opcode::BLOBBASEFEE => self.op_blobbasefee(),
Opcode::PUSH0 => self.op_push0(),
Opcode::PUSH1 => self.op_push1(),
Opcode::PUSH2 => self.op_push2(),
// Optimize push 1,2,3,4,8 and 32 at compile time due to their higher usage.
Opcode::PUSH1 => self.op_pushn::<1>(),
Opcode::PUSH2 => self.op_pushn::<2>(),
Opcode::PUSH3 => self.op_pushn::<3>(),
Opcode::PUSH4 => self.op_pushn::<4>(),
Opcode::PUSH8 => self.op_pushn::<8>(),
Opcode::PUSH32 => self.op_pushn::<32>(),
Comment thread
edg-l marked this conversation as resolved.
Outdated
// PUSHn
op if (Opcode::PUSH3..=Opcode::PUSH32).contains(&op) => {
// The following conversions and operation cannot fail due to known operand ranges.
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/levm/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
};
use ExceptionalHalt::OutOfBounds;
use ExceptionalHalt::OutOfGas;
use ethrex_common::U256;
use ethrex_common::{U256, utils::u256_from_big_endian};

/// Memory of the EVM, a volatile byte array.
pub type Memory = Vec<u8>;
Expand Down Expand Up @@ -32,7 +32,7 @@ pub fn try_resize(memory: &mut Memory, unchecked_new_size: usize) -> Result<(),
}

pub fn load_word(memory: &mut Memory, offset: U256) -> Result<U256, VMError> {
load_range(memory, offset, WORD_SIZE_IN_BYTES_USIZE).map(U256::from_big_endian)
load_range(memory, offset, WORD_SIZE_IN_BYTES_USIZE).map(u256_from_big_endian)
}

pub fn load_range(memory: &mut Memory, offset: U256, size: usize) -> Result<&[u8], VMError> {
Expand Down
9 changes: 5 additions & 4 deletions crates/vm/levm/src/opcode_handlers/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
utils::*,
vm::VM,
};
use ethrex_common::{U256, types::Fork};
use ethrex_common::{U256, types::Fork, utils::u256_from_big_endian_const};

// Block Information (11)
// Opcodes: BLOCKHASH, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE, BLOBHASH, BLOBBASEFEE
Expand Down Expand Up @@ -34,7 +34,7 @@ impl<'a> VM<'a> {
let block_hash = self.db.store.get_block_hash(block_number)?;
self.current_call_frame_mut()?
.stack
.push(&[U256::from_big_endian(block_hash.as_bytes())])?;
.push(&[u256_from_big_endian_const(block_hash.to_fixed_bytes())])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand Down Expand Up @@ -78,7 +78,8 @@ impl<'a> VM<'a> {
pub fn op_prevrandao(&mut self) -> Result<OpcodeResult, VMError> {
// https://eips.ethereum.org/EIPS/eip-4399
// After Paris the prev randao is the prev_randao (or current_random) field
let randao = U256::from_big_endian(self.env.prev_randao.unwrap_or_default().0.as_slice());
let randao =
u256_from_big_endian_const(self.env.prev_randao.unwrap_or_default().to_fixed_bytes());

let current_call_frame = self.current_call_frame_mut()?;
current_call_frame.increase_consumed_gas(gas_cost::PREVRANDAO)?;
Expand Down Expand Up @@ -160,7 +161,7 @@ impl<'a> VM<'a> {

//This should never fail because we check if the index fits above
let blob_hash = blob_hashes.get(index).ok_or(InternalError::Slicing)?;
let hash = U256::from_big_endian(blob_hash.as_bytes());
let hash = u256_from_big_endian_const(blob_hash.to_fixed_bytes());

self.current_call_frame_mut()?.stack.push(&[hash])?;

Expand Down
12 changes: 6 additions & 6 deletions crates/vm/levm/src/opcode_handlers/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
utils::word_to_address,
vm::VM,
};
use ethrex_common::U256;
use ethrex_common::{U256, utils::u256_from_big_endian_const};

// Environmental Information (16)
// Opcodes: ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, GASPRICE, EXTCODESIZE, EXTCODECOPY, RETURNDATASIZE, RETURNDATACOPY, EXTCODEHASH
Expand All @@ -20,7 +20,7 @@ impl<'a> VM<'a> {

current_call_frame
.stack
.push(&[U256::from_big_endian(addr.as_bytes())])?;
.push(&[u256_from_big_endian_const(addr.to_fixed_bytes())])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand Down Expand Up @@ -49,7 +49,7 @@ impl<'a> VM<'a> {

current_call_frame
.stack
.push(&[U256::from_big_endian(origin.as_bytes())])?;
.push(&[u256_from_big_endian_const(origin.to_fixed_bytes())])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand All @@ -62,7 +62,7 @@ impl<'a> VM<'a> {
let caller = current_call_frame.msg_sender;
current_call_frame
.stack
.push(&[U256::from_big_endian(caller.as_bytes())])?;
.push(&[u256_from_big_endian_const(caller.to_fixed_bytes())])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand Down Expand Up @@ -111,7 +111,7 @@ impl<'a> VM<'a> {
*data_byte = *byte;
}
}
let result = U256::from_big_endian(&data);
let result = u256_from_big_endian_const(data);

current_call_frame.stack.push(&[result])?;

Expand Down Expand Up @@ -402,7 +402,7 @@ impl<'a> VM<'a> {
return Ok(OpcodeResult::Continue { pc_increment: 1 });
}

let hash = U256::from_big_endian(&account_code_hash);
let hash = u256_from_big_endian_const(account_code_hash);
current_call_frame.stack.push(&[hash])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/levm/src/opcode_handlers/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
memory::{self, calculate_memory_size},
vm::VM,
};
use ethrex_common::U256;
use ethrex_common::utils::u256_from_big_endian;
use sha3::{Digest, Keccak256};

// KECCAK256 (1)
Expand Down Expand Up @@ -34,7 +34,7 @@ impl<'a> VM<'a> {
)?);
current_call_frame
.stack
.push(&[U256::from_big_endian(&hasher.finalize())])?;
.push(&[u256_from_big_endian(&hasher.finalize())])?;

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand Down
42 changes: 14 additions & 28 deletions crates/vm/levm/src/opcode_handlers/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ use crate::{
vm::VM,
};
use ExceptionalHalt::OutOfBounds;
use ethrex_common::{U256, types::Fork};
use ethrex_common::{
U256,
types::Fork,
utils::{u256_from_big_endian, u256_from_big_endian_const},
};

// Push Operations
// Opcodes: PUSH0, PUSH1 ... PUSH32
Expand All @@ -20,7 +24,7 @@ impl<'a> VM<'a> {

current_call_frame
.stack
.push(&[U256::from_big_endian(read_n_bytes)])?;
.push(&[u256_from_big_endian(read_n_bytes)])?;

// The n_bytes that you push to the stack + 1 for the next instruction
let increment_pc_by = n_bytes.wrapping_add(1);
Expand All @@ -30,39 +34,21 @@ impl<'a> VM<'a> {
})
}

/// Specialized PUSH1 operation
///
/// We use specialized push1 and push2 implementations because they are way more frequent than the others,
/// so their impact on performance is significant.
/// These implementations allow using U256::from, which is considerable more performant than U256::from_big_endian)
pub fn op_push1(&mut self) -> Result<OpcodeResult, VMError> {
let current_call_frame = self.current_call_frame_mut()?;
current_call_frame.increase_consumed_gas(gas_cost::PUSHN)?;

let value = read_bytcode_slice_const::<1>(current_call_frame)?[0];

current_call_frame.stack.push(&[U256::from(value)])?;

Ok(OpcodeResult::Continue {
// The 1 byte that you push to the stack + 1 for the next instruction
pc_increment: 2,
})
}

// Specialized PUSH2 operation
pub fn op_push2(&mut self) -> Result<OpcodeResult, VMError> {
// Generic PUSH operation, optimized at compile time for the given N.
pub fn op_pushn<const N: usize>(&mut self) -> Result<OpcodeResult, VMError> {
let current_call_frame = self.current_call_frame_mut()?;
current_call_frame.increase_consumed_gas(gas_cost::PUSHN)?;

let read_n_bytes = read_bytcode_slice_const::<2>(current_call_frame)?;
let read_n_bytes = read_bytcode_slice_const::<N>(current_call_frame)?;

let value = u16::from_be_bytes(read_n_bytes);
let value = u256_from_big_endian_const(read_n_bytes);
current_call_frame.stack.push(&[value])?;

current_call_frame.stack.push(&[U256::from(value)])?;
// The n_bytes that you push to the stack + 1 for the next instruction
let increment_pc_by = N.wrapping_add(1);

Ok(OpcodeResult::Continue {
// The 2 bytes that you push to the stack + 1 for the next instruction
pc_increment: 3,
pc_increment: increment_pc_by,
})
}

Expand Down
52 changes: 27 additions & 25 deletions crates/vm/levm/src/precompiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use bls12_381::{
};

use bytes::Bytes;
use ethrex_common::{Address, H160, H256, U256, serde_utils::bool, types::Fork};
use ethrex_common::{
Address, H160, H256, U256, serde_utils::bool, types::Fork, utils::u256_from_big_endian,
};
use keccak_hash::keccak256;
use kzg_rs::{Bytes32, Bytes48, KzgSettings};
use lambdaworks_math::{
Expand Down Expand Up @@ -303,7 +305,7 @@ pub fn ecrecover(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VME
return Ok(Bytes::new());
};

let v = U256::from_big_endian(calldata.get(32..64).ok_or(InternalError::Slicing)?);
let v = u256_from_big_endian(calldata.get(32..64).ok_or(InternalError::Slicing)?);

// The Recovery identifier is expected to be 27 or 28, any other value is invalid
if !(v == U256::from(27) || v == U256::from(28)) {
Expand Down Expand Up @@ -380,19 +382,19 @@ pub fn modexp(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMErro
// If calldata does not reach the required length, we should fill the rest with zeros
let calldata = fill_with_zeros(calldata, 96);

let base_size = U256::from_big_endian(
let base_size = u256_from_big_endian(
calldata
.get(0..32)
.ok_or(PrecompileError::ParsingInputError)?,
);

let exponent_size = U256::from_big_endian(
let exponent_size = u256_from_big_endian(
calldata
.get(32..64)
.ok_or(PrecompileError::ParsingInputError)?,
);

let modulus_size = U256::from_big_endian(
let modulus_size = u256_from_big_endian(
calldata
.get(64..96)
.ok_or(PrecompileError::ParsingInputError)?,
Expand Down Expand Up @@ -524,10 +526,10 @@ pub fn ecadd(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMError

// If points are zero the precompile should not fail, but the conversion in
// BN254Curve::create_point_from_affine will, so we verify it before the conversion
let first_point_is_zero = U256::from_big_endian(first_point_x).is_zero()
&& U256::from_big_endian(first_point_y).is_zero();
let second_point_is_zero = U256::from_big_endian(second_point_x).is_zero()
&& U256::from_big_endian(second_point_y).is_zero();
let first_point_is_zero = u256_from_big_endian(first_point_x).is_zero()
&& u256_from_big_endian(first_point_y).is_zero();
let second_point_is_zero = u256_from_big_endian(second_point_x).is_zero()
&& u256_from_big_endian(second_point_y).is_zero();

let first_point_x = BN254FieldElement::from_bytes_be(first_point_x)
.map_err(|_| PrecompileError::ParsingInputError)?;
Expand Down Expand Up @@ -565,8 +567,8 @@ pub fn ecadd(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMError
.map_err(|_| PrecompileError::ParsingInputError)?;
let sum = first_point.operate_with(&second_point).to_affine();

if U256::from_big_endian(&sum.x().to_bytes_be()) == U256::zero()
|| U256::from_big_endian(&sum.y().to_bytes_be()) == U256::zero()
if u256_from_big_endian(&sum.x().to_bytes_be()) == U256::zero()
|| u256_from_big_endian(&sum.y().to_bytes_be()) == U256::zero()
{
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
Expand Down Expand Up @@ -600,7 +602,7 @@ pub fn ecmul(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMError
// If point is zero the precompile should not fail, but the conversion in
// BN254Curve::create_point_from_affine will, so we verify it before the conversion
let point_is_zero =
U256::from_big_endian(point_x).is_zero() && U256::from_big_endian(point_y).is_zero();
u256_from_big_endian(point_x).is_zero() && u256_from_big_endian(point_y).is_zero();
if point_is_zero {
return Ok(Bytes::from([0u8; 64].to_vec()));
}
Expand All @@ -618,8 +620,8 @@ pub fn ecmul(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMError
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
let mul = point.operate_with_self(scalar).to_affine();
if U256::from_big_endian(&mul.x().to_bytes_be()) == U256::zero()
|| U256::from_big_endian(&mul.y().to_bytes_be()) == U256::zero()
if u256_from_big_endian(&mul.x().to_bytes_be()) == U256::zero()
|| u256_from_big_endian(&mul.y().to_bytes_be()) == U256::zero()
{
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
Expand Down Expand Up @@ -647,8 +649,8 @@ fn parse_first_point_coordinates(input_data: &[u8]) -> Result<FirstPointCoordina
let first_point_y = input_data.get(32..64).ok_or(InternalError::Slicing)?;

// Infinite is defined by (0,0). Any other zero-combination is invalid
if (U256::from_big_endian(first_point_x) == U256::zero())
^ (U256::from_big_endian(first_point_y) == U256::zero())
if (u256_from_big_endian(first_point_x) == U256::zero())
^ (u256_from_big_endian(first_point_y) == U256::zero())
{
return Err(PrecompileError::DefaultError.into());
}
Expand All @@ -675,8 +677,8 @@ fn parse_second_point_coordinates(
let second_point_x_second_part = input_data.get(64..96).ok_or(InternalError::Slicing)?;

// Infinite is defined by (0,0). Any other zero-combination is invalid
if (U256::from_big_endian(second_point_x_first_part) == U256::zero())
^ (U256::from_big_endian(second_point_x_second_part) == U256::zero())
if (u256_from_big_endian(second_point_x_first_part) == U256::zero())
^ (u256_from_big_endian(second_point_x_second_part) == U256::zero())
{
return Err(PrecompileError::DefaultError.into());
}
Expand All @@ -685,17 +687,17 @@ fn parse_second_point_coordinates(
let second_point_y_second_part = input_data.get(128..160).ok_or(InternalError::Slicing)?;

// Infinite is defined by (0,0). Any other zero-combination is invalid
if (U256::from_big_endian(second_point_y_first_part) == U256::zero())
^ (U256::from_big_endian(second_point_y_second_part) == U256::zero())
if (u256_from_big_endian(second_point_y_first_part) == U256::zero())
^ (u256_from_big_endian(second_point_y_second_part) == U256::zero())
{
return Err(PrecompileError::DefaultError.into());
}

// Check if the second point belongs to the curve (this happens if it's lower than the prime)
if U256::from_big_endian(second_point_x_first_part) >= ALT_BN128_PRIME
|| U256::from_big_endian(second_point_x_second_part) >= ALT_BN128_PRIME
|| U256::from_big_endian(second_point_y_first_part) >= ALT_BN128_PRIME
|| U256::from_big_endian(second_point_y_second_part) >= ALT_BN128_PRIME
if u256_from_big_endian(second_point_x_first_part) >= ALT_BN128_PRIME
|| u256_from_big_endian(second_point_x_second_part) >= ALT_BN128_PRIME
|| u256_from_big_endian(second_point_y_first_part) >= ALT_BN128_PRIME
|| u256_from_big_endian(second_point_y_second_part) >= ALT_BN128_PRIME
{
return Err(PrecompileError::DefaultError.into());
}
Expand Down Expand Up @@ -1012,7 +1014,7 @@ pub fn blake2f(calldata: &Bytes, gas_remaining: &mut u64) -> Result<Bytes, VMErr
return Err(PrecompileError::ParsingInputError.into());
}

let rounds = U256::from_big_endian(calldata.get(0..4).ok_or(InternalError::Slicing)?);
let rounds = u256_from_big_endian(calldata.get(0..4).ok_or(InternalError::Slicing)?);

let rounds: usize = rounds
.try_into()
Expand Down
Loading
Loading