Skip to content

Commit 1130d27

Browse files
iovoidilitterijrchatruc
authored
perf(l1): avoid opening tries (#5145)
**Motivation** Currently to get a storage we are: * getting the block header to obtain the state root * getting the account to get the storage root * reading the value The first step isn't needed because the state root is the same through the block, and the second isn't needed because since we're using a path-based DB we can directly get the value (without even using the trie, if the FKVs are computed). **Description** We make the VM database ask by state_root instead of block hash (skipping step 1), and we skip reading the account storage root when the FlatKeyValues are computed for it. --------- Co-authored-by: Ivan Litteri <[email protected]> Co-authored-by: Javier Chatruc <[email protected]> Co-authored-by: Javier Rodríguez Chatruc <[email protected]>
1 parent a9cd9b7 commit 1130d27

File tree

20 files changed

+181
-89
lines changed

20 files changed

+181
-89
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Perf
44

55
### 2025-10-31
6-
6+
- Reduce overhead of trie opening [#5145](https://github.com/lambdaclass/ethrex/pull/5145)
77
- Improved discovery and peer initialization [#5147](https://github.com/lambdaclass/ethrex/pull/5147)
88

99
### 2025-10-30

crates/blockchain/blockchain.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ impl Blockchain {
165165
// Validate the block pre-execution
166166
validate_block(block, &parent_header, &chain_config, ELASTICITY_MULTIPLIER)?;
167167

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

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

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

214214
let exec_merkle_start = Instant::now();
@@ -532,14 +532,19 @@ impl Blockchain {
532532

533533
for (i, block) in blocks.iter().enumerate() {
534534
let parent_hash = block.header.parent_hash;
535+
let parent_header = self
536+
.storage
537+
.get_block_header_by_hash(parent_hash)
538+
.map_err(ChainError::StoreError)?
539+
.ok_or(ChainError::ParentNotFound)?;
535540

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

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

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

1015+
let parent_header = self
1016+
.storage
1017+
.get_block_header_by_hash(first_block_header.parent_hash)
1018+
.map_err(|e| (ChainError::StoreError(e), None))?
1019+
.ok_or((ChainError::ParentNotFound, None))?;
10101020
let vm_db = StoreVmDatabase::new_with_block_hash_cache(
10111021
self.storage.clone(),
1012-
first_block_header.parent_hash,
1022+
parent_header,
10131023
block_hash_cache,
10141024
);
10151025
let mut vm = self.new_evm(vm_db).map_err(|e| (e.into(), None))?;

crates/blockchain/payload.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ impl PayloadBuildContext {
235235
.unwrap_or_default(),
236236
);
237237

238-
let vm_db = StoreVmDatabase::new(storage.clone(), payload.header.parent_hash);
238+
let parent_header = storage
239+
.get_block_header_by_hash(payload.header.parent_hash)
240+
.map_err(|e| EvmError::DB(e.to_string()))?
241+
.ok_or_else(|| EvmError::DB("parent header not found".to_string()))?;
242+
let vm_db = StoreVmDatabase::new(storage.clone(), parent_header);
239243
let vm = new_evm(blockchain_type, vm_db)?;
240244

241245
let payload_size = payload.encode_to_vec().len() as u64;

crates/blockchain/tracing.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,13 @@ impl Blockchain {
102102
.iter()
103103
.map(|b| (b.header.number, b.hash()))
104104
.collect();
105+
let parent_header = self
106+
.storage
107+
.get_block_header_by_hash(parent_hash)?
108+
.ok_or(ChainError::ParentNotFound)?;
105109
let vm_db = StoreVmDatabase::new_with_block_hash_cache(
106110
self.storage.clone(),
107-
parent_hash,
111+
parent_header,
108112
block_hash_cache,
109113
);
110114
let mut vm = self.new_evm(vm_db)?;

crates/blockchain/vm.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use ethrex_common::{
22
Address, H256, U256,
33
constants::EMPTY_KECCACK_HASH,
4-
types::{AccountState, BlockHash, BlockNumber, ChainConfig, Code},
4+
types::{AccountState, BlockHash, BlockHeader, BlockNumber, ChainConfig, Code},
55
};
66
use ethrex_storage::Store;
77
use ethrex_vm::{EvmError, VmDatabase};
@@ -16,26 +16,29 @@ pub struct StoreVmDatabase {
1616
// We use this when executing blocks in batches, as we will only add the blocks at the end
1717
// And may need to access hashes of blocks previously executed in the batch
1818
pub block_hash_cache: HashMap<BlockNumber, BlockHash>,
19+
pub state_root: H256,
1920
}
2021

2122
impl StoreVmDatabase {
22-
pub fn new(store: Store, block_hash: BlockHash) -> Self {
23+
pub fn new(store: Store, block_header: BlockHeader) -> Self {
2324
StoreVmDatabase {
2425
store,
25-
block_hash,
26+
block_hash: block_header.hash(),
2627
block_hash_cache: HashMap::new(),
28+
state_root: block_header.state_root,
2729
}
2830
}
2931

3032
pub fn new_with_block_hash_cache(
3133
store: Store,
32-
block_hash: BlockHash,
34+
block_header: BlockHeader,
3335
block_hash_cache: HashMap<BlockNumber, BlockHash>,
3436
) -> Self {
3537
StoreVmDatabase {
3638
store,
37-
block_hash,
39+
block_hash: block_header.hash(),
3840
block_hash_cache,
41+
state_root: block_header.state_root,
3942
}
4043
}
4144
}
@@ -44,14 +47,14 @@ impl VmDatabase for StoreVmDatabase {
4447
#[instrument(level = "trace", name = "Account read", skip_all)]
4548
fn get_account_state(&self, address: Address) -> Result<Option<AccountState>, EvmError> {
4649
self.store
47-
.get_account_state_by_hash(self.block_hash, address)
50+
.get_account_state_by_root(self.state_root, address)
4851
.map_err(|e| EvmError::DB(e.to_string()))
4952
}
5053

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

crates/l2/based/block_fetcher.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{cmp::min, collections::HashMap, sync::Arc, time::Duration};
22

3+
use ethrex_blockchain::error::ChainError;
34
use ethrex_blockchain::{Blockchain, fork_choice::apply_fork_choice, vm::StoreVmDatabase};
45
use ethrex_common::utils::keccak;
56
use ethrex_common::{
@@ -359,7 +360,11 @@ impl BlockFetcher {
359360
// This is copied from the L1Committer, this should be reviewed.
360361
let mut acc_account_updates: HashMap<H160, AccountUpdate> = HashMap::new();
361362
for block in batch {
362-
let vm_db = StoreVmDatabase::new(self.store.clone(), block.header.parent_hash);
363+
let parent_header = self
364+
.store
365+
.get_block_header_by_hash(block.header.parent_hash)?
366+
.ok_or(BlockFetcherError::ChainError(ChainError::ParentNotFound))?;
367+
let vm_db = StoreVmDatabase::new(self.store.clone(), parent_header);
363368
let mut vm = self.blockchain.new_evm(vm_db)?;
364369
vm.execute_block(block)
365370
.map_err(BlockFetcherError::EvmError)?;
@@ -378,8 +383,12 @@ impl BlockFetcher {
378383
}
379384

380385
let parent_block_hash = first_block.header.parent_hash;
386+
let parent_header = self
387+
.store
388+
.get_block_header_by_hash(parent_block_hash)?
389+
.ok_or(BlockFetcherError::ChainError(ChainError::ParentNotFound))?;
381390

382-
let parent_db = StoreVmDatabase::new(self.store.clone(), parent_block_hash);
391+
let parent_db = StoreVmDatabase::new(self.store.clone(), parent_header);
383392

384393
let state_diff = prepare_state_diff(
385394
last_block.header.clone(),

crates/l2/sequencer/l1_committer.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
};
1212
use bytes::Bytes;
1313
use ethrex_blockchain::{
14-
Blockchain, BlockchainOptions, BlockchainType, L2Config, vm::StoreVmDatabase,
14+
Blockchain, BlockchainOptions, BlockchainType, L2Config, error::ChainError, vm::StoreVmDatabase,
1515
};
1616
use ethrex_common::{
1717
Address, H256, U256,
@@ -534,13 +534,14 @@ impl L1Committer {
534534
"Could not find execution cache result for block {}, falling back to re-execution",
535535
last_added_block_number + 1
536536
);
537+
let parent_header = self
538+
.store
539+
.get_block_header_by_hash(potential_batch_block.header.parent_hash)?
540+
.ok_or(CommitterError::ChainError(ChainError::ParentNotFound))?;
537541

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

545546
let fee_config = self
546547
.rollup_store
@@ -603,9 +604,14 @@ impl L1Committer {
603604
))?
604605
.parent_hash;
605606

607+
let parent_header = self
608+
.store
609+
.get_block_header_by_hash(parent_block_hash)?
610+
.ok_or(CommitterError::ChainError(ChainError::ParentNotFound))?;
611+
606612
// Again, here the VM database should be instantiated from the checkpoint
607613
// store to have access to the previous state
608-
let parent_db = StoreVmDatabase::new(checkpoint_store.clone(), parent_block_hash);
614+
let parent_db = StoreVmDatabase::new(checkpoint_store.clone(), parent_header);
609615

610616
let acc_privileged_txs_len: u64 = acc_privileged_txs.len().try_into()?;
611617
if acc_privileged_txs_len > PRIVILEGED_TX_BUDGET {

crates/networking/rpc/eth/account.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@ impl RpcHandler for GetStorageAtRequest {
136136

137137
let storage_value = context
138138
.storage
139-
.get_storage_at(block_number, self.address, self.storage_slot)
140-
.await?
139+
.get_storage_at(block_number, self.address, self.storage_slot)?
141140
.unwrap_or_default();
142141
let storage_value = H256::from_uint(&storage_value);
143142
serde_json::to_value(format!("{storage_value:#x}"))

crates/networking/rpc/eth/transaction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ impl RpcHandler for CreateAccessListRequest {
347347
_ => return Ok(Value::Null),
348348
};
349349

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

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

577577
match vm.simulate_tx_from_generic(transaction, block_header)? {

crates/storage/api.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,4 +380,8 @@ pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe {
380380
fn generate_flatkeyvalue(&self) -> Result<(), StoreError>;
381381

382382
async fn create_checkpoint(&self, path: &Path) -> Result<(), StoreError>;
383+
384+
fn flatkeyvalue_computed(&self, _account: H256) -> Result<bool, StoreError> {
385+
Ok(false)
386+
}
383387
}

0 commit comments

Comments
 (0)