Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5b9a49a
reduce the overhead added by opening tries
iovoid Oct 31, 2025
6282207
fix initial value
iovoid Oct 31, 2025
f7d5446
use 64 byte fkv-done marker
iovoid Oct 31, 2025
bcfcc62
avoid fetching block header
iovoid Oct 31, 2025
9919ee0
also set last_computed_flatkeyvalue when finishing
iovoid Oct 31, 2025
f9ca53a
support old fkv marker
iovoid Oct 31, 2025
54e29d2
fix old marker support
iovoid Oct 31, 2025
cdcb5ae
fix marker length
iovoid Oct 31, 2025
3208eb9
do not change marker
iovoid Oct 31, 2025
a0f8d87
unify last_written initialization
iovoid Oct 31, 2025
b0c592e
update perf changelog
iovoid Oct 31, 2025
6d24eff
update bencher
iovoid Oct 31, 2025
f154e24
Merge branch 'main' into avoid_opening_trie
iovoid Oct 31, 2025
214dfd0
fix state root
iovoid Oct 31, 2025
e4d55e4
update more users of StoreVmDatabase
iovoid Oct 31, 2025
37d9fa5
remove unneeded await
iovoid Oct 31, 2025
173b6e0
order writes to avoid errors
iovoid Oct 31, 2025
5ddd6d7
Merge branch 'main' into avoid_opening_trie
iovoid Oct 31, 2025
11340ab
fix docs
iovoid Oct 31, 2025
ac872a6
fmt tooling
iovoid Oct 31, 2025
9ab3a81
Merge branch 'main' into avoid_opening_trie
ilitteri Oct 31, 2025
d8b5931
fix witness generation
iovoid Oct 31, 2025
e2df98a
Merge branch 'main' into avoid_opening_trie
jrchatruc Nov 3, 2025
d3918f8
fix merge
iovoid Nov 3, 2025
631a076
fix merge in blockchain
iovoid Nov 3, 2025
f3eef26
Merge branch 'main' into avoid_opening_trie
jrchatruc Nov 3, 2025
d4234f4
set flatkeyvalue_computed default implementation
iovoid Nov 3, 2025
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Perf

### 2025-10-31

- Reduce overhead of trie opening [#5145](https://github.com/lambdaclass/ethrex/pull/5145)
- Improved discovery and peer initialization [#5147](https://github.com/lambdaclass/ethrex/pull/5147)

### 2025-10-30
Expand Down
18 changes: 14 additions & 4 deletions crates/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl Blockchain {
// Validate the block pre-execution
validate_block(block, &parent_header, &chain_config, ELASTICITY_MULTIPLIER)?;

let vm_db = StoreVmDatabase::new(self.storage.clone(), block.header.parent_hash);
let vm_db = StoreVmDatabase::new(self.storage.clone(), parent_header);
let mut vm = self.new_evm(vm_db)?;

let execution_result = vm.execute_block(block)?;
Expand Down Expand Up @@ -208,7 +208,7 @@ impl Blockchain {
validate_block(block, &parent_header, &chain_config, ELASTICITY_MULTIPLIER)?;
let block_validated_instant = Instant::now();

let vm_db = StoreVmDatabase::new(self.storage.clone(), block.header.parent_hash);
let vm_db = StoreVmDatabase::new(self.storage.clone(), parent_header.clone());
let mut vm = self.new_evm(vm_db)?;

let exec_merkle_start = Instant::now();
Expand Down Expand Up @@ -532,14 +532,19 @@ impl Blockchain {

for (i, block) in blocks.iter().enumerate() {
let parent_hash = block.header.parent_hash;
let parent_header = self
.storage
.get_block_header_by_hash(parent_hash)
.map_err(ChainError::StoreError)?
.ok_or(ChainError::ParentNotFound)?;

// This assumes that the user has the necessary state stored already,
// so if the user only has the state previous to the first block, it
// will fail in the second iteration of this for loop. To ensure this,
// doesn't fail, later in this function we store the new state after
// re-execution.
let vm_db: DynVmDatabase =
Box::new(StoreVmDatabase::new(self.storage.clone(), parent_hash));
Box::new(StoreVmDatabase::new(self.storage.clone(), parent_header));

let logger = Arc::new(DatabaseLogger::new(Arc::new(Mutex::new(Box::new(vm_db)))));

Expand Down Expand Up @@ -1007,9 +1012,14 @@ impl Blockchain {
// Cache block hashes for the full batch so we can access them during execution without having to store the blocks beforehand
let block_hash_cache = blocks.iter().map(|b| (b.header.number, b.hash())).collect();

let parent_header = self
.storage
.get_block_header_by_hash(first_block_header.parent_hash)
.map_err(|e| (ChainError::StoreError(e), None))?
.ok_or((ChainError::ParentNotFound, None))?;
let vm_db = StoreVmDatabase::new_with_block_hash_cache(
self.storage.clone(),
first_block_header.parent_hash,
parent_header,
block_hash_cache,
);
let mut vm = self.new_evm(vm_db).map_err(|e| (e.into(), None))?;
Expand Down
6 changes: 5 additions & 1 deletion crates/blockchain/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ impl PayloadBuildContext {
.unwrap_or_default(),
);

let vm_db = StoreVmDatabase::new(storage.clone(), payload.header.parent_hash);
let parent_header = storage
.get_block_header_by_hash(payload.header.parent_hash)
.map_err(|e| EvmError::DB(e.to_string()))?
.ok_or_else(|| EvmError::DB("parent header not found".to_string()))?;
let vm_db = StoreVmDatabase::new(storage.clone(), parent_header);
let vm = new_evm(blockchain_type, vm_db)?;

let payload_size = payload.encode_to_vec().len() as u64;
Expand Down
6 changes: 5 additions & 1 deletion crates/blockchain/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,13 @@ impl Blockchain {
.iter()
.map(|b| (b.header.number, b.hash()))
.collect();
let parent_header = self
.storage
.get_block_header_by_hash(parent_hash)?
.ok_or(ChainError::ParentNotFound)?;
let vm_db = StoreVmDatabase::new_with_block_hash_cache(
self.storage.clone(),
parent_hash,
parent_header,
block_hash_cache,
);
let mut vm = self.new_evm(vm_db)?;
Expand Down
17 changes: 10 additions & 7 deletions crates/blockchain/vm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ethrex_common::{
Address, H256, U256,
constants::EMPTY_KECCACK_HASH,
types::{AccountState, BlockHash, BlockNumber, ChainConfig, Code},
types::{AccountState, BlockHash, BlockHeader, BlockNumber, ChainConfig, Code},
};
use ethrex_storage::Store;
use ethrex_vm::{EvmError, VmDatabase};
Expand All @@ -16,26 +16,29 @@ pub struct StoreVmDatabase {
// We use this when executing blocks in batches, as we will only add the blocks at the end
// And may need to access hashes of blocks previously executed in the batch
pub block_hash_cache: HashMap<BlockNumber, BlockHash>,
pub state_root: H256,
}

impl StoreVmDatabase {
pub fn new(store: Store, block_hash: BlockHash) -> Self {
pub fn new(store: Store, block_header: BlockHeader) -> Self {
StoreVmDatabase {
store,
block_hash,
block_hash: block_header.hash(),
block_hash_cache: HashMap::new(),
state_root: block_header.state_root,
}
}

pub fn new_with_block_hash_cache(
store: Store,
block_hash: BlockHash,
block_header: BlockHeader,
block_hash_cache: HashMap<BlockNumber, BlockHash>,
) -> Self {
StoreVmDatabase {
store,
block_hash,
block_hash: block_header.hash(),
block_hash_cache,
state_root: block_header.state_root,
}
}
}
Expand All @@ -44,14 +47,14 @@ impl VmDatabase for StoreVmDatabase {
#[instrument(level = "trace", name = "Account read", skip_all)]
fn get_account_state(&self, address: Address) -> Result<Option<AccountState>, EvmError> {
self.store
.get_account_state_by_hash(self.block_hash, address)
.get_account_state_by_root(self.state_root, address)
.map_err(|e| EvmError::DB(e.to_string()))
}

#[instrument(level = "trace", name = "Storage read", skip_all)]
fn get_storage_slot(&self, address: Address, key: H256) -> Result<Option<U256>, EvmError> {
self.store
.get_storage_at_hash(self.block_hash, address, key)
.get_storage_at_root(self.state_root, address, key)
.map_err(|e| EvmError::DB(e.to_string()))
}

Expand Down
13 changes: 11 additions & 2 deletions crates/l2/based/block_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{cmp::min, collections::HashMap, sync::Arc, time::Duration};

use ethrex_blockchain::error::ChainError;
use ethrex_blockchain::{Blockchain, fork_choice::apply_fork_choice, vm::StoreVmDatabase};
use ethrex_common::utils::keccak;
use ethrex_common::{
Expand Down Expand Up @@ -359,7 +360,11 @@ impl BlockFetcher {
// This is copied from the L1Committer, this should be reviewed.
let mut acc_account_updates: HashMap<H160, AccountUpdate> = HashMap::new();
for block in batch {
let vm_db = StoreVmDatabase::new(self.store.clone(), block.header.parent_hash);
let parent_header = self
.store
.get_block_header_by_hash(block.header.parent_hash)?
.ok_or(BlockFetcherError::ChainError(ChainError::ParentNotFound))?;
let vm_db = StoreVmDatabase::new(self.store.clone(), parent_header);
let mut vm = self.blockchain.new_evm(vm_db)?;
vm.execute_block(block)
.map_err(BlockFetcherError::EvmError)?;
Expand All @@ -378,8 +383,12 @@ impl BlockFetcher {
}

let parent_block_hash = first_block.header.parent_hash;
let parent_header = self
.store
.get_block_header_by_hash(parent_block_hash)?
.ok_or(BlockFetcherError::ChainError(ChainError::ParentNotFound))?;

let parent_db = StoreVmDatabase::new(self.store.clone(), parent_block_hash);
let parent_db = StoreVmDatabase::new(self.store.clone(), parent_header);

let state_diff = prepare_state_diff(
last_block.header.clone(),
Expand Down
18 changes: 12 additions & 6 deletions crates/l2/sequencer/l1_committer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
};
use bytes::Bytes;
use ethrex_blockchain::{
Blockchain, BlockchainOptions, BlockchainType, L2Config, vm::StoreVmDatabase,
Blockchain, BlockchainOptions, BlockchainType, L2Config, error::ChainError, vm::StoreVmDatabase,
};
use ethrex_common::{
Address, H256, U256,
Expand Down Expand Up @@ -534,13 +534,14 @@ impl L1Committer {
"Could not find execution cache result for block {}, falling back to re-execution",
last_added_block_number + 1
);
let parent_header = self
.store
.get_block_header_by_hash(potential_batch_block.header.parent_hash)?
.ok_or(CommitterError::ChainError(ChainError::ParentNotFound))?;

// Here we use the checkpoint store because we need the previous
// state available (i.e. not pruned) for re-execution.
let vm_db = StoreVmDatabase::new(
checkpoint_store.clone(),
potential_batch_block.header.parent_hash,
);
let vm_db = StoreVmDatabase::new(checkpoint_store.clone(), parent_header);

let fee_config = self
.rollup_store
Expand Down Expand Up @@ -603,9 +604,14 @@ impl L1Committer {
))?
.parent_hash;

let parent_header = self
.store
.get_block_header_by_hash(parent_block_hash)?
.ok_or(CommitterError::ChainError(ChainError::ParentNotFound))?;

// Again, here the VM database should be instantiated from the checkpoint
// store to have access to the previous state
let parent_db = StoreVmDatabase::new(checkpoint_store.clone(), parent_block_hash);
let parent_db = StoreVmDatabase::new(checkpoint_store.clone(), parent_header);

let acc_privileged_txs_len: u64 = acc_privileged_txs.len().try_into()?;
if acc_privileged_txs_len > PRIVILEGED_TX_BUDGET {
Expand Down
3 changes: 1 addition & 2 deletions crates/networking/rpc/eth/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ impl RpcHandler for GetStorageAtRequest {

let storage_value = context
.storage
.get_storage_at(block_number, self.address, self.storage_slot)
.await?
.get_storage_at(block_number, self.address, self.storage_slot)?
.unwrap_or_default();
let storage_value = H256::from_uint(&storage_value);
serde_json::to_value(format!("{storage_value:#x}"))
Expand Down
4 changes: 2 additions & 2 deletions crates/networking/rpc/eth/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ impl RpcHandler for CreateAccessListRequest {
_ => return Ok(Value::Null),
};

let vm_db = StoreVmDatabase::new(context.storage.clone(), header.hash());
let vm_db = StoreVmDatabase::new(context.storage.clone(), header.clone());
let mut vm = context.blockchain.new_evm(vm_db)?;

// Run transaction and obtain access list
Expand Down Expand Up @@ -571,7 +571,7 @@ async fn simulate_tx(
storage: Store,
blockchain: Arc<Blockchain>,
) -> Result<ExecutionResult, RpcErr> {
let vm_db = StoreVmDatabase::new(storage, block_header.hash());
let vm_db = StoreVmDatabase::new(storage, block_header.clone());
let mut vm = blockchain.new_evm(vm_db)?;

match vm.simulate_tx_from_generic(transaction, block_header)? {
Expand Down
4 changes: 4 additions & 0 deletions crates/storage/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,8 @@ pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe {
fn generate_flatkeyvalue(&self) -> Result<(), StoreError>;

async fn create_checkpoint(&self, path: &Path) -> Result<(), StoreError>;

fn flatkeyvalue_computed(&self, _account: H256) -> Result<bool, StoreError> {
Ok(false)
}
}
34 changes: 22 additions & 12 deletions crates/storage/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,27 +712,39 @@ impl Store {
self.engine.get_block_by_number(block_number).await
}

pub async fn get_storage_at(
pub fn get_storage_at(
&self,
block_number: BlockNumber,
address: Address,
storage_key: H256,
) -> Result<Option<U256>, StoreError> {
match self.get_canonical_block_hash(block_number).await? {
Some(block_hash) => self.get_storage_at_hash(block_hash, address, storage_key),
match self.get_block_header(block_number)? {
Some(header) => self.get_storage_at_root(header.state_root, address, storage_key),
None => Ok(None),
}
}

pub fn get_storage_at_hash(
pub fn get_storage_at_root(
&self,
block_hash: BlockHash,
state_root: H256,
address: Address,
storage_key: H256,
) -> Result<Option<U256>, StoreError> {
let Some(storage_trie) = self.storage_trie(block_hash, address)? else {
return Ok(None);
let hashed_address = hash_address(&address);
let account_hash = H256::from_slice(&hashed_address);
let storage_root = if self.engine.flatkeyvalue_computed(account_hash)? {
// We will use FKVs, we don't need the root
*EMPTY_TRIE_HASH
} else {
let state_trie = self.open_state_trie(state_root)?;
let Some(encoded_account) = state_trie.get(&hashed_address)? else {
return Ok(None);
};
let account = AccountState::decode(&encoded_account)?;
account.storage_root
};
let storage_trie = self.open_storage_trie(account_hash, storage_root, state_root)?;

let hashed_key = hash_key(&storage_key);
storage_trie
.get(&hashed_key)?
Expand Down Expand Up @@ -883,14 +895,12 @@ impl Store {
get_account_state_from_trie(&state_trie, address)
}

pub fn get_account_state_by_hash(
pub fn get_account_state_by_root(
&self,
block_hash: BlockHash,
state_root: H256,
address: Address,
) -> Result<Option<AccountState>, StoreError> {
let Some(state_trie) = self.state_trie(block_hash)? else {
return Ok(None);
};
let state_trie = self.open_state_trie(state_root)?;
self.get_account_state_from_trie(&state_trie, address)
}

Expand Down
Loading