From 5b9a49add09575dff4c10d729a7164739e52d3d0 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 15:31:21 -0300 Subject: [PATCH 01/22] reduce the overhead added by opening tries --- crates/blockchain/vm.rs | 15 ++++++- crates/networking/rpc/eth/account.rs | 3 +- crates/storage/api.rs | 2 + crates/storage/store.rs | 34 ++++++++++------ crates/storage/store_db/in_memory.rs | 4 ++ crates/storage/store_db/rocksdb.rs | 52 ++++++++++++++++++++++-- crates/storage/trie_db/rocksdb.rs | 31 ++++---------- crates/storage/trie_db/rocksdb_locked.rs | 16 ++------ 8 files changed, 103 insertions(+), 54 deletions(-) diff --git a/crates/blockchain/vm.rs b/crates/blockchain/vm.rs index 6a5b90cb28e..d8ffc1649d6 100644 --- a/crates/blockchain/vm.rs +++ b/crates/blockchain/vm.rs @@ -40,18 +40,29 @@ impl StoreVmDatabase { } } +impl StoreVmDatabase { + fn get_state_root(&self) -> Result { + Ok(self + .store + .get_block_header_by_hash(self.block_hash) + .map_err(|e| EvmError::DB(e.to_string()))? + .ok_or_else(|| EvmError::DB("parent header missing".to_string()))? + .state_root) + } +} + impl VmDatabase for StoreVmDatabase { #[instrument(level = "trace", name = "Account read", skip_all)] fn get_account_state(&self, address: Address) -> Result, EvmError> { self.store - .get_account_state_by_hash(self.block_hash, address) + .get_account_state_by_root(self.get_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, EvmError> { self.store - .get_storage_at_hash(self.block_hash, address, key) + .get_storage_at_root(self.get_state_root()?, address, key) .map_err(|e| EvmError::DB(e.to_string())) } diff --git a/crates/networking/rpc/eth/account.rs b/crates/networking/rpc/eth/account.rs index bcc6fd1f25c..749ba32f93b 100644 --- a/crates/networking/rpc/eth/account.rs +++ b/crates/networking/rpc/eth/account.rs @@ -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}")) diff --git a/crates/storage/api.rs b/crates/storage/api.rs index 5de722cd3e0..00772c675d9 100644 --- a/crates/storage/api.rs +++ b/crates/storage/api.rs @@ -380,4 +380,6 @@ 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; } diff --git a/crates/storage/store.rs b/crates/storage/store.rs index 4357c74e4c0..be37565ffd1 100644 --- a/crates/storage/store.rs +++ b/crates/storage/store.rs @@ -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, 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, 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)? @@ -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, 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) } diff --git a/crates/storage/store_db/in_memory.rs b/crates/storage/store_db/in_memory.rs index 811a56328c3..15407c83d17 100644 --- a/crates/storage/store_db/in_memory.rs +++ b/crates/storage/store_db/in_memory.rs @@ -739,6 +739,10 @@ impl StoreEngine for Store { // Silently ignoring the request to create a checkpoint is harmless Ok(()) } + + fn flatkeyvalue_computed(&self, _account: H256) -> Result { + Ok(false) + } } impl Debug for Store { diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index f3e000cbdc5..c9b18773a33 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -142,6 +142,7 @@ pub struct Store { trie_cache: Arc>>, flatkeyvalue_control_tx: std::sync::mpsc::SyncSender, trie_update_worker_tx: TriedUpdateWorkerTx, + last_computed_flatkeyvalue: Arc>>, } impl Store { @@ -347,6 +348,7 @@ impl Store { trie_cache: Default::default(), flatkeyvalue_control_tx: fkv_tx, trie_update_worker_tx: trie_upd_tx, + last_computed_flatkeyvalue: Default::default(), }; let store_clone = store.clone(); std::thread::spawn(move || { @@ -562,6 +564,10 @@ impl Store { .db .get_cf(&cf_misc, "last_written")? .unwrap_or_default(); + *self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)? = last_written.clone(); if last_written == vec![0xff] { return Ok(()); } @@ -607,6 +613,10 @@ impl Store { batch.put_cf(&cf_flatkeyvalue, path.as_ref(), node.value); ctr += 1; if ctr > 10_000 { + *self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)? = path.as_ref().to_vec(); self.db.write(std::mem::take(&mut batch))?; } @@ -626,6 +636,10 @@ impl Store { batch.put_cf(&cf_flatkeyvalue, key.as_ref(), node.value); ctr += 1; if ctr > 10_000 { + *self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)? = key.as_ref().to_vec(); self.db.write(std::mem::take(&mut batch))?; } if let Ok(value) = control_rx.try_recv() { @@ -748,6 +762,14 @@ impl Store { *trie_cache.lock().map_err(|_| StoreError::LockError)? = Arc::new(trie_mut); Ok(()) } + + fn last_written(&self) -> Result, StoreError> { + let last_computed_flatkeyvalue = self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)?; + Ok(last_computed_flatkeyvalue.clone()) + } } #[async_trait::async_trait] @@ -1437,7 +1459,12 @@ impl StoreEngine for Store { state_root: H256, ) -> Result { // FIXME: use a DB snapshot here - let db = Box::new(RocksDBTrieDB::new(self.db.clone(), CF_TRIE_NODES, None)?); + let db = Box::new(RocksDBTrieDB::new( + self.db.clone(), + CF_TRIE_NODES, + None, + self.last_written()?, + )?); let wrap_db = Box::new(TrieWrapper { state_root, inner: self @@ -1453,7 +1480,12 @@ impl StoreEngine for Store { fn open_state_trie(&self, state_root: H256) -> Result { // FIXME: use a DB snapshot here - let db = Box::new(RocksDBTrieDB::new(self.db.clone(), CF_TRIE_NODES, None)?); + let db = Box::new(RocksDBTrieDB::new( + self.db.clone(), + CF_TRIE_NODES, + None, + self.last_written()?, + )?); let wrap_db = Box::new(TrieWrapper { state_root, inner: self @@ -1476,12 +1508,18 @@ impl StoreEngine for Store { self.db.clone(), CF_TRIE_NODES, Some(hashed_address), + self.last_written()?, )?); Ok(Trie::open(db, storage_root)) } fn open_direct_state_trie(&self, state_root: H256) -> Result { - let db = Box::new(RocksDBTrieDB::new(self.db.clone(), CF_TRIE_NODES, None)?); + let db = Box::new(RocksDBTrieDB::new( + self.db.clone(), + CF_TRIE_NODES, + None, + self.last_written()?, + )?); Ok(Trie::open(db, state_root)) } @@ -1490,6 +1528,7 @@ impl StoreEngine for Store { self.db.clone(), CF_TRIE_NODES, None, + self.last_written()?, )?); let wrap_db = Box::new(TrieWrapper { state_root, @@ -1514,6 +1553,7 @@ impl StoreEngine for Store { self.db.clone(), CF_TRIE_NODES, None, + self.last_written()?, )?); let wrap_db = Box::new(TrieWrapper { state_root, @@ -1901,6 +1941,12 @@ impl StoreEngine for Store { Ok(()) } + + fn flatkeyvalue_computed(&self, account: H256) -> Result { + let account_nibbles = Nibbles::from_bytes(account.as_bytes()); + let last_computed_flatkeyvalue = self.last_written()?; + Ok(&last_computed_flatkeyvalue[0..64] > account_nibbles.as_ref()) + } } /// Open column families diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index acf852f60b9..629f8044666 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -4,10 +4,7 @@ use ethrex_trie::{Nibbles, Node, TrieDB, error::TrieError}; use rocksdb::{DBWithThreadMode, MultiThreaded}; use std::sync::Arc; -use crate::{ - store_db::rocksdb::{CF_FLATKEYVALUE, CF_MISC_VALUES}, - trie_db::layering::apply_prefix, -}; +use crate::{store_db::rocksdb::CF_FLATKEYVALUE, trie_db::layering::apply_prefix}; /// RocksDB implementation for the TrieDB trait, with get and put operations. pub struct RocksDBTrieDB { @@ -26,6 +23,7 @@ impl RocksDBTrieDB { db: Arc>, cf_name: &str, address_prefix: Option, + last_written: Vec, ) -> Result { // Verify column family exists if db.cf_handle(cf_name).is_none() { @@ -34,15 +32,7 @@ impl RocksDBTrieDB { cf_name ))); } - let cf_misc = db - .cf_handle(CF_MISC_VALUES) - .ok_or_else(|| TrieError::DbError(anyhow::anyhow!("Column family not found")))?; - let last_computed_flatkeyvalue = db - .get_cf(&cf_misc, "last_written") - .map_err(|e| TrieError::DbError(anyhow::anyhow!("Error reading last_written: {e}")))? - .map(|v| Nibbles::from_hex(v.to_vec())) - .unwrap_or_default(); - drop(cf_misc); + let last_computed_flatkeyvalue = Nibbles::from_hex(last_written); Ok(Self { db, @@ -155,18 +145,17 @@ mod tests { db_options.create_missing_column_families(true); let cf_descriptor = ColumnFamilyDescriptor::new("test_cf", Options::default()); - let cf_misc = ColumnFamilyDescriptor::new(CF_MISC_VALUES, Options::default()); let cf_fkv = ColumnFamilyDescriptor::new(CF_FLATKEYVALUE, Options::default()); let db = DBWithThreadMode::::open_cf_descriptors( &db_options, db_path, - vec![cf_descriptor, cf_misc, cf_fkv], + vec![cf_descriptor, cf_fkv], ) .unwrap(); let db = Arc::new(db); // Create TrieDB - let trie_db = RocksDBTrieDB::new(db, "test_cf", None).unwrap(); + let trie_db = RocksDBTrieDB::new(db, "test_cf", None, vec![]).unwrap(); // Test data let node_hash = Nibbles::from_hex(vec![1]); @@ -196,20 +185,19 @@ mod tests { db_options.create_if_missing(true); db_options.create_missing_column_families(true); - let cf_misc = ColumnFamilyDescriptor::new(CF_MISC_VALUES, Options::default()); let cf_descriptor = ColumnFamilyDescriptor::new("test_cf", Options::default()); let cf_fkv = ColumnFamilyDescriptor::new(CF_FLATKEYVALUE, Options::default()); let db = DBWithThreadMode::::open_cf_descriptors( &db_options, db_path, - vec![cf_descriptor, cf_misc, cf_fkv], + vec![cf_descriptor, cf_fkv], ) .unwrap(); let db = Arc::new(db); // Create TrieDB with address prefix let address = H256::from([0xaa; 32]); - let trie_db = RocksDBTrieDB::new(db, "test_cf", Some(address)).unwrap(); + let trie_db = RocksDBTrieDB::new(db, "test_cf", Some(address), vec![]).unwrap(); // Test data let node_hash = Nibbles::from_hex(vec![1]); @@ -235,19 +223,18 @@ mod tests { db_options.create_if_missing(true); db_options.create_missing_column_families(true); - let cf_misc = ColumnFamilyDescriptor::new(CF_MISC_VALUES, Options::default()); let cf_descriptor = ColumnFamilyDescriptor::new("test_cf", Options::default()); let cf_fkv = ColumnFamilyDescriptor::new(CF_FLATKEYVALUE, Options::default()); let db = DBWithThreadMode::::open_cf_descriptors( &db_options, db_path, - vec![cf_descriptor, cf_misc, cf_fkv], + vec![cf_descriptor, cf_fkv], ) .unwrap(); let db = Arc::new(db); // Create TrieDB - let trie_db = RocksDBTrieDB::new(db, "test_cf", None).unwrap(); + let trie_db = RocksDBTrieDB::new(db, "test_cf", None, vec![]).unwrap(); // Test data // NOTE: we don't use the same paths to avoid overwriting in the batch diff --git a/crates/storage/trie_db/rocksdb_locked.rs b/crates/storage/trie_db/rocksdb_locked.rs index 3f7b83f692e..274bcf1b19e 100644 --- a/crates/storage/trie_db/rocksdb_locked.rs +++ b/crates/storage/trie_db/rocksdb_locked.rs @@ -3,10 +3,7 @@ use ethrex_trie::{Nibbles, TrieDB, error::TrieError}; use rocksdb::{DBWithThreadMode, MultiThreaded, SnapshotWithThreadMode}; use std::sync::Arc; -use crate::{ - store_db::rocksdb::{CF_FLATKEYVALUE, CF_MISC_VALUES}, - trie_db::layering::apply_prefix, -}; +use crate::{store_db::rocksdb::CF_FLATKEYVALUE, trie_db::layering::apply_prefix}; /// RocksDB locked implementation for the TrieDB trait, read-only with consistent snapshot. pub struct RocksDBLockedTrieDB { @@ -28,6 +25,7 @@ impl RocksDBLockedTrieDB { db: Arc>, cf_name: &str, address_prefix: Option, + last_written: Vec, ) -> Result { // Leak the database reference to get 'static lifetime let db = Box::leak(Box::new(db)); @@ -41,15 +39,7 @@ impl RocksDBLockedTrieDB { TrieError::DbError(anyhow::anyhow!("Column family not found: {}", cf_name)) })?; - let cf_misc = db - .cf_handle(CF_MISC_VALUES) - .ok_or_else(|| TrieError::DbError(anyhow::anyhow!("Column family not found")))?; - let last_computed_flatkeyvalue = db - .get_cf(&cf_misc, "last_written") - .map_err(|e| TrieError::DbError(anyhow::anyhow!("Error reading last_written: {e}")))? - .map(|v| Nibbles::from_hex(v.to_vec())) - .unwrap_or_default(); - drop(cf_misc); + let last_computed_flatkeyvalue = Nibbles::from_hex(last_written); // Create snapshot for consistent reads let snapshot = db.snapshot(); From 6282207d842398918744f1233966537caba6810a Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 15:35:27 -0300 Subject: [PATCH 02/22] fix initial value --- crates/storage/store_db/rocksdb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index c9b18773a33..cbff6659068 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -348,7 +348,7 @@ impl Store { trie_cache: Default::default(), flatkeyvalue_control_tx: fkv_tx, trie_update_worker_tx: trie_upd_tx, - last_computed_flatkeyvalue: Default::default(), + last_computed_flatkeyvalue: Arc::new(Mutex::new(vec![0u8; 64])), }; let store_clone = store.clone(); std::thread::spawn(move || { From f7d5446ee81ec5ef4397ef2ffe9599b4594e7ace Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 15:41:42 -0300 Subject: [PATCH 03/22] use 64 byte fkv-done marker --- crates/storage/store_db/rocksdb.rs | 6 +++--- docs/developers/l1/flatkeyvalue.md | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index cbff6659068..a1e9e82f889 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -568,12 +568,12 @@ impl Store { .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = last_written.clone(); - if last_written == vec![0xff] { + if last_written == vec![0xff; 64] { return Ok(()); } self.db - .delete_range_cf(&cf_flatkeyvalue, last_written, vec![0xff])?; + .delete_range_cf(&cf_flatkeyvalue, last_written, vec![0xff; 64])?; loop { let root = self @@ -677,7 +677,7 @@ impl Store { } Err(err) => return Err(err), Ok(()) => { - batch.put_cf(&cf_misc, "last_written", [0xff]); + batch.put_cf(&cf_misc, "last_written", [0xff; 64]); self.db.write(batch)?; return Ok(()); } diff --git a/docs/developers/l1/flatkeyvalue.md b/docs/developers/l1/flatkeyvalue.md index 8459fc72b62..88015a5635f 100644 --- a/docs/developers/l1/flatkeyvalue.md +++ b/docs/developers/l1/flatkeyvalue.md @@ -32,5 +32,3 @@ In particular, you might want to run ``` to know how advanced the progress is. - -A value of 0xff marks it's complete. From bcfcc62a0794b4c0e94c5882443deb00556884db Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 16:09:35 -0300 Subject: [PATCH 04/22] avoid fetching block header --- crates/blockchain/blockchain.rs | 16 +++++++++++--- crates/blockchain/payload.rs | 6 ++++- crates/blockchain/tracing.rs | 6 ++++- crates/blockchain/vm.rs | 28 +++++++++--------------- crates/l2/based/block_fetcher.rs | 13 +++++++++-- crates/l2/sequencer/l1_committer.rs | 19 ++++++++++------ crates/networking/rpc/eth/transaction.rs | 4 ++-- crates/vm/levm/runner/src/main.rs | 6 ++--- 8 files changed, 61 insertions(+), 37 deletions(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index bfe33e4c3d4..3b248a73029 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -155,7 +155,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)?; @@ -256,6 +256,11 @@ 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(first_block_header.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 @@ -263,7 +268,7 @@ impl Blockchain { // 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))))); @@ -636,9 +641,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))?; diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index 3497bbabe10..df80f286258 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -234,7 +234,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)?; Ok(PayloadBuildContext { diff --git a/crates/blockchain/tracing.rs b/crates/blockchain/tracing.rs index 445b98ffe84..5f049ce183b 100644 --- a/crates/blockchain/tracing.rs +++ b/crates/blockchain/tracing.rs @@ -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)?; diff --git a/crates/blockchain/vm.rs b/crates/blockchain/vm.rs index d8ffc1649d6..7a5def34969 100644 --- a/crates/blockchain/vm.rs +++ b/crates/blockchain/vm.rs @@ -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}; @@ -16,53 +16,45 @@ 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, + 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, ) -> Self { StoreVmDatabase { store, - block_hash, + block_hash: block_header.hash(), block_hash_cache, + state_root: block_header.state_root, } } } -impl StoreVmDatabase { - fn get_state_root(&self) -> Result { - Ok(self - .store - .get_block_header_by_hash(self.block_hash) - .map_err(|e| EvmError::DB(e.to_string()))? - .ok_or_else(|| EvmError::DB("parent header missing".to_string()))? - .state_root) - } -} - impl VmDatabase for StoreVmDatabase { #[instrument(level = "trace", name = "Account read", skip_all)] fn get_account_state(&self, address: Address) -> Result, EvmError> { self.store - .get_account_state_by_root(self.get_state_root()?, 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, EvmError> { self.store - .get_storage_at_root(self.get_state_root()?, address, key) + .get_storage_at_root(self.state_root, address, key) .map_err(|e| EvmError::DB(e.to_string())) } diff --git a/crates/l2/based/block_fetcher.rs b/crates/l2/based/block_fetcher.rs index 39f871a92e3..bbd526300f6 100644 --- a/crates/l2/based/block_fetcher.rs +++ b/crates/l2/based/block_fetcher.rs @@ -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::{ @@ -357,7 +358,11 @@ impl BlockFetcher { // This is copied from the L1Committer, this should be reviewed. let mut acc_account_updates: HashMap = 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)?; @@ -376,8 +381,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(), diff --git a/crates/l2/sequencer/l1_committer.rs b/crates/l2/sequencer/l1_committer.rs index 4a385239b1d..28fd5d9761b 100644 --- a/crates/l2/sequencer/l1_committer.rs +++ b/crates/l2/sequencer/l1_committer.rs @@ -10,7 +10,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, @@ -517,13 +517,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( - one_time_checkpoint_store.clone(), - potential_batch_block.header.parent_hash, - ); + let vm_db = StoreVmDatabase::new(one_time_checkpoint_store.clone(), parent_header); let fee_config = self .rollup_store @@ -586,10 +587,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(one_time_checkpoint_store.clone(), parent_block_hash); + let parent_db = StoreVmDatabase::new(one_time_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 { diff --git a/crates/networking/rpc/eth/transaction.rs b/crates/networking/rpc/eth/transaction.rs index 8b5e30e540d..de456083e05 100644 --- a/crates/networking/rpc/eth/transaction.rs +++ b/crates/networking/rpc/eth/transaction.rs @@ -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 @@ -571,7 +571,7 @@ async fn simulate_tx( storage: Store, blockchain: Arc, ) -> Result { - 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)? { diff --git a/crates/vm/levm/runner/src/main.rs b/crates/vm/levm/runner/src/main.rs index 71e1c484f94..eedf9bcf901 100644 --- a/crates/vm/levm/runner/src/main.rs +++ b/crates/vm/levm/runner/src/main.rs @@ -3,8 +3,8 @@ use clap::Parser; use env_logger::Env; use ethrex_blockchain::vm::StoreVmDatabase; use ethrex_common::{ - Address, H160, H256, U256, - types::{Account, Code, LegacyTransaction, Transaction}, + Address, H160, U256, + types::{Account, BlockHeader, Code, LegacyTransaction, Transaction}, }; use ethrex_levm::{ EVMConfig, Environment, @@ -141,7 +141,7 @@ fn main() { // DB let initial_state = setup_initial_state(&mut runner_input, bytecode); let in_memory_db = Store::new("", ethrex_storage::EngineType::InMemory).unwrap(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, H256::zero())); + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, BlockHeader::default())); let mut db = GeneralizedDatabase::new_with_account_state(Arc::new(store), initial_state); // Initialize VM From 9919ee0636befc8937fd48f728baf0686a668ac2 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 16:18:58 -0300 Subject: [PATCH 05/22] also set last_computed_flatkeyvalue when finishing --- crates/storage/store_db/rocksdb.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index a1e9e82f889..f6fa8428443 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -678,6 +678,10 @@ impl Store { Err(err) => return Err(err), Ok(()) => { batch.put_cf(&cf_misc, "last_written", [0xff; 64]); + *self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)? = vec![0xff; 64]; self.db.write(batch)?; return Ok(()); } From f9ca53a9823a9afce105bd9561352992f78eec88 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 16:39:19 -0300 Subject: [PATCH 06/22] support old fkv marker --- crates/storage/store_db/rocksdb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index f6fa8428443..d729caad939 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -568,7 +568,7 @@ impl Store { .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = last_written.clone(); - if last_written == vec![0xff; 64] { + if last_written.first() == Some(&0xff) { return Ok(()); } From 54e29d2d2e19db57792c71eb69f600edce0a2680 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 16:50:35 -0300 Subject: [PATCH 07/22] fix old marker support --- crates/storage/store_db/rocksdb.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index d729caad939..4ba19ce6e62 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -569,6 +569,10 @@ impl Store { .lock() .map_err(|_| StoreError::LockError)? = last_written.clone(); if last_written.first() == Some(&0xff) { + *self + .last_computed_flatkeyvalue + .lock() + .map_err(|_| StoreError::LockError)? = vec![0xff]; return Ok(()); } From cdcb5ae50ea3351a329c4b4d56259e6d2b0fb31f Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 16:54:46 -0300 Subject: [PATCH 08/22] fix marker length --- crates/storage/store_db/rocksdb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index 4ba19ce6e62..8380c86529b 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -572,7 +572,7 @@ impl Store { *self .last_computed_flatkeyvalue .lock() - .map_err(|_| StoreError::LockError)? = vec![0xff]; + .map_err(|_| StoreError::LockError)? = vec![0xff; 64]; return Ok(()); } From 3208eb9f141f33ff81ac841a105659960d3e36a7 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:08:24 -0300 Subject: [PATCH 09/22] do not change marker --- crates/storage/store_db/rocksdb.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index 8380c86529b..484d8245b77 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -568,7 +568,7 @@ impl Store { .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = last_written.clone(); - if last_written.first() == Some(&0xff) { + if last_written == vec![0xff] { *self .last_computed_flatkeyvalue .lock() @@ -577,7 +577,7 @@ impl Store { } self.db - .delete_range_cf(&cf_flatkeyvalue, last_written, vec![0xff; 64])?; + .delete_range_cf(&cf_flatkeyvalue, last_written, vec![0xff])?; loop { let root = self @@ -681,7 +681,7 @@ impl Store { } Err(err) => return Err(err), Ok(()) => { - batch.put_cf(&cf_misc, "last_written", [0xff; 64]); + batch.put_cf(&cf_misc, "last_written", [0xff]); *self .last_computed_flatkeyvalue .lock() From a0f8d877f23c33d68d46a4cf557f9d4de460ac4b Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:23:29 -0300 Subject: [PATCH 10/22] unify last_written initialization --- crates/storage/store_db/rocksdb.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index 484d8245b77..d534b7c4039 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -343,12 +343,23 @@ impl Store { let (fkv_tx, fkv_rx) = std::sync::mpsc::sync_channel(0); let (trie_upd_tx, trie_upd_rx) = std::sync::mpsc::sync_channel(0); + let cf_misc = db + .cf_handle(CF_MISC_VALUES) + .ok_or_else(|| StoreError::Custom("column not found".to_string()))?; + let mut last_written = db + .get_cf(&cf_misc, "last_written")? + .unwrap_or_else(|| vec![0u8; 64]); + if last_written == vec![0xff] { + last_written = vec![0xff; 64]; + } + drop(cf_misc); // dropped to remove borrow on db + let store = Self { db: Arc::new(db), trie_cache: Default::default(), flatkeyvalue_control_tx: fkv_tx, trie_update_worker_tx: trie_upd_tx, - last_computed_flatkeyvalue: Arc::new(Mutex::new(vec![0u8; 64])), + last_computed_flatkeyvalue: Arc::new(Mutex::new(last_written)), }; let store_clone = store.clone(); std::thread::spawn(move || { @@ -564,15 +575,7 @@ impl Store { .db .get_cf(&cf_misc, "last_written")? .unwrap_or_default(); - *self - .last_computed_flatkeyvalue - .lock() - .map_err(|_| StoreError::LockError)? = last_written.clone(); if last_written == vec![0xff] { - *self - .last_computed_flatkeyvalue - .lock() - .map_err(|_| StoreError::LockError)? = vec![0xff; 64]; return Ok(()); } From b0c592ee8f4895cc1bdd571d0c1cbc93bf5f0061 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:26:12 -0300 Subject: [PATCH 11/22] update perf changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b3e959b126..d65dbb51af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Perf +### 2025-10-31 +- Reduce overhead of trie opening [#5145](https://github.com/lambdaclass/ethrex/pull/5145) + ### 2025-10-28 - Batch BlobsBundle::validate [#4993](https://github.com/lambdaclass/ethrex/pull/4993) From 6d24eff1018ce69e64e6002e1849a7587c1b6310 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:29:17 -0300 Subject: [PATCH 12/22] update bencher --- crates/vm/levm/bench/revm_comparison/src/levm_bench.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs b/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs index a56437e0610..4e0b24288eb 100644 --- a/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs +++ b/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs @@ -1,9 +1,8 @@ use bytes::Bytes; use ethrex_blockchain::vm::StoreVmDatabase; -use ethrex_common::H256; use ethrex_common::{ Address, U256, - types::{Account, Code, EIP1559Transaction, Transaction, TxKind}, + types::{Account, BlockHeader, Code, EIP1559Transaction, Transaction, TxKind}, }; use ethrex_levm::errors::VMError; use ethrex_levm::{ @@ -53,7 +52,7 @@ pub fn run_with_levm(contract_code: &str, runs: u64, calldata: &str) { fn init_db(bytecode: Bytes) -> GeneralizedDatabase { // The store type for this bench shouldn't matter as all operations use the LEVM cache let in_memory_db = Store::new("", ethrex_storage::EngineType::InMemory).unwrap(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, H256::zero())); + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, BlockHeader::default())); let cache = BTreeMap::from([ ( From 214dfd0be6f60f112086b6135209b38ea5da734a Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:42:03 -0300 Subject: [PATCH 13/22] fix state root --- crates/vm/levm/bench/revm_comparison/src/levm_bench.rs | 7 ++++++- crates/vm/levm/runner/src/main.rs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs b/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs index 4e0b24288eb..a4e9eef0902 100644 --- a/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs +++ b/crates/vm/levm/bench/revm_comparison/src/levm_bench.rs @@ -2,6 +2,7 @@ use bytes::Bytes; use ethrex_blockchain::vm::StoreVmDatabase; use ethrex_common::{ Address, U256, + constants::EMPTY_TRIE_HASH, types::{Account, BlockHeader, Code, EIP1559Transaction, Transaction, TxKind}, }; use ethrex_levm::errors::VMError; @@ -52,7 +53,11 @@ pub fn run_with_levm(contract_code: &str, runs: u64, calldata: &str) { fn init_db(bytecode: Bytes) -> GeneralizedDatabase { // The store type for this bench shouldn't matter as all operations use the LEVM cache let in_memory_db = Store::new("", ethrex_storage::EngineType::InMemory).unwrap(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, BlockHeader::default())); + let header = BlockHeader { + state_root: *EMPTY_TRIE_HASH, + ..Default::default() + }; + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, header)); let cache = BTreeMap::from([ ( diff --git a/crates/vm/levm/runner/src/main.rs b/crates/vm/levm/runner/src/main.rs index eedf9bcf901..935e4f6a1b3 100644 --- a/crates/vm/levm/runner/src/main.rs +++ b/crates/vm/levm/runner/src/main.rs @@ -4,6 +4,7 @@ use env_logger::Env; use ethrex_blockchain::vm::StoreVmDatabase; use ethrex_common::{ Address, H160, U256, + constants::EMPTY_TRIE_HASH, types::{Account, BlockHeader, Code, LegacyTransaction, Transaction}, }; use ethrex_levm::{ @@ -141,7 +142,11 @@ fn main() { // DB let initial_state = setup_initial_state(&mut runner_input, bytecode); let in_memory_db = Store::new("", ethrex_storage::EngineType::InMemory).unwrap(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, BlockHeader::default())); + let header = BlockHeader { + state_root: *EMPTY_TRIE_HASH, + ..Default::default() + }; + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(in_memory_db, header)); let mut db = GeneralizedDatabase::new_with_account_state(Arc::new(store), initial_state); // Initialize VM From e4d55e451eae781f8443952be2a0a4b94d10da68 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:52:37 -0300 Subject: [PATCH 14/22] update more users of StoreVmDatabase --- docs/developers/l1/importing-blocks.md | 2 +- tooling/ef_tests/state/utils.rs | 6 ++---- tooling/ef_tests/state_v2/src/modules/utils.rs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/developers/l1/importing-blocks.md b/docs/developers/l1/importing-blocks.md index aed399cddd5..0ec6d3a3b0d 100644 --- a/docs/developers/l1/importing-blocks.md +++ b/docs/developers/l1/importing-blocks.md @@ -96,7 +96,7 @@ As mentioned in the previous point, the VM execution doesn't directly mutate the This is a key piece of code in `Blockchain.execute_block`: ```rust -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 = Evm::new(vm_db); let execution_result = vm.execute_block(block)?; let account_updates = vm.get_state_transitions()?; diff --git a/tooling/ef_tests/state/utils.rs b/tooling/ef_tests/state/utils.rs index 08c7ca2c953..45683308c69 100644 --- a/tooling/ef_tests/state/utils.rs +++ b/tooling/ef_tests/state/utils.rs @@ -22,7 +22,7 @@ pub async fn load_initial_state_revm(test: &EFTest) -> (RevmState, H256, Store) let vm_db: DynVmDatabase = Box::new(StoreVmDatabase::new( storage.clone(), - genesis.get_block().hash(), + genesis.get_block().header, )); (revm_state(vm_db), genesis.get_block().hash(), storage) @@ -35,9 +35,7 @@ pub async fn load_initial_state_levm(test: &EFTest) -> GeneralizedDatabase { let mut storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store"); storage.add_initial_state(genesis.clone()).await.unwrap(); - let block_hash = genesis.get_block().hash(); - - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(storage, block_hash)); + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(storage, genesis.get_block().header)); GeneralizedDatabase::new(Arc::new(store)) } diff --git a/tooling/ef_tests/state_v2/src/modules/utils.rs b/tooling/ef_tests/state_v2/src/modules/utils.rs index 076ebcda185..d55e6530669 100644 --- a/tooling/ef_tests/state_v2/src/modules/utils.rs +++ b/tooling/ef_tests/state_v2/src/modules/utils.rs @@ -45,7 +45,7 @@ pub async fn load_initial_state( storage.add_initial_state(genesis.clone()).await.unwrap(); let block_hash = genesis.get_block().hash(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(storage.clone(), block_hash)); + let store: DynVmDatabase = Box::new(StoreVmDatabase::new(storage.clone(), genesis.get_block().header)); // We return some values that will be needed to calculate the post execution checks (original storage, genesis and blockhash) ( From 37d9fa570bb606e6180d061766b32e879b95464a Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:53:53 -0300 Subject: [PATCH 15/22] remove unneeded await --- tooling/ef_tests/blockchain/test_runner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tooling/ef_tests/blockchain/test_runner.rs b/tooling/ef_tests/blockchain/test_runner.rs index d0c5b9d10a6..4f261831af7 100644 --- a/tooling/ef_tests/blockchain/test_runner.rs +++ b/tooling/ef_tests/blockchain/test_runner.rs @@ -353,7 +353,6 @@ async fn check_poststate_against_db(test_key: &str, test: &TestUnit, db: &Store) for (key, value) in expected_account.storage { let db_storage_value = db .get_storage_at(latest_block_number, *addr, key) - .await .expect("Failed to read from DB") .unwrap_or_else(|| { panic!("Storage missing for address {addr} key {key} in DB test:{test_key}") From 173b6e05795b763a81e6dd6e6b390356033993e3 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 17:59:47 -0300 Subject: [PATCH 16/22] order writes to avoid errors --- crates/storage/store_db/rocksdb.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index d534b7c4039..faae647d248 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -620,11 +620,11 @@ impl Store { batch.put_cf(&cf_flatkeyvalue, path.as_ref(), node.value); ctr += 1; if ctr > 10_000 { + self.db.write(std::mem::take(&mut batch))?; *self .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = path.as_ref().to_vec(); - self.db.write(std::mem::take(&mut batch))?; } let mut iter_inner = self @@ -643,11 +643,11 @@ impl Store { batch.put_cf(&cf_flatkeyvalue, key.as_ref(), node.value); ctr += 1; if ctr > 10_000 { + self.db.write(std::mem::take(&mut batch))?; *self .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = key.as_ref().to_vec(); - self.db.write(std::mem::take(&mut batch))?; } if let Ok(value) = control_rx.try_recv() { match value { @@ -685,11 +685,11 @@ impl Store { Err(err) => return Err(err), Ok(()) => { batch.put_cf(&cf_misc, "last_written", [0xff]); + self.db.write(batch)?; *self .last_computed_flatkeyvalue .lock() .map_err(|_| StoreError::LockError)? = vec![0xff; 64]; - self.db.write(batch)?; return Ok(()); } }; From 11340abbd328b8b4a43a8705b0fa2a692c4259c3 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 18:05:42 -0300 Subject: [PATCH 17/22] fix docs --- docs/developers/l1/flatkeyvalue.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/developers/l1/flatkeyvalue.md b/docs/developers/l1/flatkeyvalue.md index 88015a5635f..8459fc72b62 100644 --- a/docs/developers/l1/flatkeyvalue.md +++ b/docs/developers/l1/flatkeyvalue.md @@ -32,3 +32,5 @@ In particular, you might want to run ``` to know how advanced the progress is. + +A value of 0xff marks it's complete. From ac872a6828a028f856c367429b5cc04cf6ff5343 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 18:25:22 -0300 Subject: [PATCH 18/22] fmt tooling --- tooling/ef_tests/state_v2/src/modules/utils.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tooling/ef_tests/state_v2/src/modules/utils.rs b/tooling/ef_tests/state_v2/src/modules/utils.rs index d55e6530669..1dfa3497bf6 100644 --- a/tooling/ef_tests/state_v2/src/modules/utils.rs +++ b/tooling/ef_tests/state_v2/src/modules/utils.rs @@ -45,7 +45,10 @@ pub async fn load_initial_state( storage.add_initial_state(genesis.clone()).await.unwrap(); let block_hash = genesis.get_block().hash(); - let store: DynVmDatabase = Box::new(StoreVmDatabase::new(storage.clone(), genesis.get_block().header)); + let store: DynVmDatabase = Box::new(StoreVmDatabase::new( + storage.clone(), + genesis.get_block().header, + )); // We return some values that will be needed to calculate the post execution checks (original storage, genesis and blockhash) ( From d8b59313d5ed316a808ef1c0b433ffc95458cf24 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Fri, 31 Oct 2025 19:53:49 -0300 Subject: [PATCH 19/22] fix witness generation --- crates/blockchain/blockchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 3b248a73029..81fb3c43fa3 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -258,7 +258,7 @@ impl Blockchain { let parent_hash = block.header.parent_hash; let parent_header = self .storage - .get_block_header_by_hash(first_block_header.parent_hash) + .get_block_header_by_hash(parent_hash) .map_err(ChainError::StoreError)? .ok_or(ChainError::ParentNotFound)?; From d3918f8e4f812e3052e72f415ff7fd67045d946a Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Mon, 3 Nov 2025 09:12:33 -0300 Subject: [PATCH 20/22] fix merge --- crates/l2/sequencer/l1_committer.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/l2/sequencer/l1_committer.rs b/crates/l2/sequencer/l1_committer.rs index a455ecf12d1..0cdc5bfe9e7 100644 --- a/crates/l2/sequencer/l1_committer.rs +++ b/crates/l2/sequencer/l1_committer.rs @@ -541,10 +541,7 @@ impl L1Committer { // 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 @@ -614,7 +611,7 @@ impl L1Committer { // 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 { From 631a076c850d3226d0e0e2e1cb9a2ef8291d1a04 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Mon, 3 Nov 2025 09:57:14 -0300 Subject: [PATCH 21/22] fix merge in blockchain --- crates/blockchain/blockchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 9e39d7f9218..6fe402f0c88 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -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.clone()); + 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(); From d4234f47a7e247c475c8eafc4982781f5221f7d3 Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Mon, 3 Nov 2025 14:43:07 -0300 Subject: [PATCH 22/22] set flatkeyvalue_computed default implementation --- crates/storage/api.rs | 4 +++- crates/storage/store_db/in_memory.rs | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/storage/api.rs b/crates/storage/api.rs index 00772c675d9..664f72612ae 100644 --- a/crates/storage/api.rs +++ b/crates/storage/api.rs @@ -381,5 +381,7 @@ pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe { async fn create_checkpoint(&self, path: &Path) -> Result<(), StoreError>; - fn flatkeyvalue_computed(&self, account: H256) -> Result; + fn flatkeyvalue_computed(&self, _account: H256) -> Result { + Ok(false) + } } diff --git a/crates/storage/store_db/in_memory.rs b/crates/storage/store_db/in_memory.rs index 15407c83d17..811a56328c3 100644 --- a/crates/storage/store_db/in_memory.rs +++ b/crates/storage/store_db/in_memory.rs @@ -739,10 +739,6 @@ impl StoreEngine for Store { // Silently ignoring the request to create a checkpoint is harmless Ok(()) } - - fn flatkeyvalue_computed(&self, _account: H256) -> Result { - Ok(false) - } } impl Debug for Store {