Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Perf

### 2025-11-26

- Avoid unnecessary hashing of init codes and already hashed codes [#5397](https://github.com/lambdaclass/ethrex/pull/5397)

### 2025-11-19

- Parallelize merkleization [#5377](https://github.com/lambdaclass/ethrex/pull/5377)
Expand Down
19 changes: 15 additions & 4 deletions crates/common/types/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ pub struct Code {
}

impl Code {
// TODO: also add `from_hashed_bytecode` to optimize the download pipeline,
// where hash is already known and checked.
// SAFETY: hash will be stored as-is, so it either needs to match
// the real code hash (i.e. it was precomputed and we're reusing)
// or never be read (e.g. for initcode).
pub fn from_bytecode_unchecked(code: Bytes, hash: H256) -> Self {
let jump_targets = Self::compute_jump_targets(&code);
Self {
hash,
bytecode: code,
jump_targets,
}
}

pub fn from_bytecode(code: Bytes) -> Self {
let jump_targets = Self::compute_jump_targets(&code);
Self {
Expand Down Expand Up @@ -143,13 +153,14 @@ impl Default for Code {

impl From<GenesisAccount> for Account {
fn from(genesis: GenesisAccount) -> Self {
let code = Code::from_bytecode(genesis.code);
Self {
info: AccountInfo {
code_hash: code_hash(&genesis.code),
code_hash: code.hash,
balance: genesis.balance,
nonce: genesis.nonce,
},
code: Code::from_bytecode(genesis.code),
code,
storage: genesis
.storage
.iter()
Expand Down
10 changes: 8 additions & 2 deletions crates/networking/p2p/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,11 @@ impl Syncer {
.write_account_code_batch(
code_hashes_to_download
.drain(..)
.zip(bytecodes.into_iter().map(Code::from_bytecode))
.zip(bytecodes)
// SAFETY: hash already checked by the download worker
.map(|(hash, code)| {
(hash, Code::from_bytecode_unchecked(code, hash))
})
.collect(),
)
.await?;
Expand All @@ -950,7 +954,9 @@ impl Syncer {
.write_account_code_batch(
code_hashes_to_download
.drain(..)
.zip(bytecodes.into_iter().map(Code::from_bytecode))
.zip(bytecodes)
// SAFETY: hash already checked by the download worker
.map(|(hash, code)| (hash, Code::from_bytecode_unchecked(code, hash)))
.collect(),
)
.await?;
Expand Down
7 changes: 4 additions & 3 deletions crates/vm/levm/runner/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ethrex_common::serde_utils::bytes::deserialize;
use ethrex_common::serde_utils::u64;
use ethrex_common::serde_utils::u256;
use ethrex_common::types::Code;
use ethrex_common::types::{Account, AccountInfo, code_hash};
use ethrex_common::types::{Account, AccountInfo};
use ethrex_common::{Address, U256, types::Fork};
use serde::Deserialize;
use std::collections::HashMap;
Expand Down Expand Up @@ -35,13 +35,14 @@ pub struct InputAccount {

impl From<InputAccount> for Account {
fn from(account: InputAccount) -> Self {
let code = Code::from_bytecode(account.code);
Account {
info: AccountInfo {
code_hash: code_hash(&account.code),
code_hash: code.hash,
balance: account.balance,
nonce: 0,
},
code: Code::from_bytecode(account.code),
code,
storage: account
.storage
.into_iter()
Expand Down
8 changes: 6 additions & 2 deletions crates/vm/levm/src/hooks/default_hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{

use bytes::Bytes;
use ethrex_common::{
Address, U256,
Address, H256, U256,
types::{Code, Fork},
};

Expand Down Expand Up @@ -494,7 +494,11 @@ pub fn set_bytecode_and_code_address(vm: &mut VM<'_>) -> Result<(), VMError> {
let (bytecode, code_address) = if vm.is_create()? {
// Here bytecode is the calldata and the code_address is just the created contract address.
let calldata = std::mem::take(&mut vm.current_call_frame.calldata);
(Code::from_bytecode(calldata), vm.current_call_frame.to)
(
// SAFETY: we don't need the hash for the initcode
Code::from_bytecode_unchecked(calldata, H256::zero()),
vm.current_call_frame.to,
)
} else {
// Here bytecode and code_address could be either from the account or from the delegated account.
let to = vm.current_call_frame.to;
Expand Down
5 changes: 3 additions & 2 deletions crates/vm/levm/src/opcode_handlers/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
vm::VM,
};
use bytes::Bytes;
use ethrex_common::{Address, U256, evm::calculate_create_address, types::Fork};
use ethrex_common::{Address, H256, U256, evm::calculate_create_address, types::Fork};
use ethrex_common::{
tracing::CallType::{self, CALL, CALLCODE, DELEGATECALL, SELFDESTRUCT, STATICCALL},
types::Code,
Expand Down Expand Up @@ -682,7 +682,8 @@ impl<'a> VM<'a> {
deployer,
new_address,
new_address,
Code::from_bytecode(code),
// SAFETY: init code hash is never used
Code::from_bytecode_unchecked(code, H256::zero()),
value,
Bytes::new(),
false,
Expand Down