Skip to content

Commit 658c837

Browse files
committed
Merge branch 'main' into partial_dirtying
2 parents 4bbd481 + 120e1c7 commit 658c837

5 files changed

Lines changed: 171 additions & 103 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### 2025-11-12
66

77
- Only mark individual values as dirty instead of the whole trie [#5282](https://github.com/lambdaclass/ethrex/pull/5282)
8+
- Separate Account and storage Column families in rocksdb [#5055](https://github.com/lambdaclass/ethrex/pull/5055)
89
- Avoid copying while reading account code [#5289](https://github.com/lambdaclass/ethrex/pull/5289)
910
- Cache `BLOBBASEFEE` opcode value [#5288](https://github.com/lambdaclass/ethrex/pull/5288)
1011

crates/storage/store.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
use crate::api::StoreEngine;
12
use crate::error::StoreError;
23
use crate::store_db::in_memory::Store as InMemoryStore;
34
#[cfg(feature = "rocksdb")]
45
use crate::store_db::rocksdb::Store as RocksDBStore;
5-
use crate::{api::StoreEngine, apply_prefix};
66

77
use ethereum_types::{Address, H256, U256};
88
use ethrex_common::{
@@ -533,50 +533,43 @@ impl Store {
533533
&self,
534534
genesis_accounts: BTreeMap<Address, GenesisAccount>,
535535
) -> Result<H256, StoreError> {
536-
let mut nodes = HashMap::new();
537-
let mut genesis_state_trie = self.engine.open_direct_state_trie(*EMPTY_TRIE_HASH)?;
536+
let mut account_trie = self.engine.open_direct_state_trie(*EMPTY_TRIE_HASH)?;
537+
538538
for (address, account) in genesis_accounts {
539539
let hashed_address = hash_address(&address);
540+
540541
// Store account code (as this won't be stored in the trie)
541542
let code = Code::from_bytecode(account.code);
542543
let code_hash = code.hash;
543544
self.add_account_code(code).await?;
545+
544546
// Store the account's storage in a clean storage trie and compute its root
545547
let mut storage_trie = self
546548
.engine
547549
.open_direct_storage_trie(H256::from_slice(&hashed_address), *EMPTY_TRIE_HASH)?;
550+
548551
for (storage_key, storage_value) in account.storage {
549552
if !storage_value.is_zero() {
550553
let hashed_key = hash_key(&H256(storage_key.to_big_endian()));
551554
storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
552555
}
553556
}
554-
let (storage_root, new_nodes) = storage_trie.collect_changes_since_last_hash();
555-
nodes.insert(H256::from_slice(&hashed_address), new_nodes);
557+
558+
// TODO(#5195): committing each storage trie individually is inefficient.
559+
// We would benefit form a mass storage node insertion method.
560+
556561
// Add account to trie
557562
let account_state = AccountState {
558563
nonce: account.nonce,
559564
balance: account.balance,
560-
storage_root,
565+
storage_root: storage_trie.hash()?,
561566
code_hash,
562567
};
563-
genesis_state_trie.insert(hashed_address, account_state.encode_to_vec())?;
568+
569+
account_trie.insert(hashed_address, account_state.encode_to_vec())?;
564570
}
565-
let (state_root, state_nodes) = genesis_state_trie.collect_changes_since_last_hash();
566-
567-
// TODO: replace this with a Store method
568-
genesis_state_trie.db().put_batch(
569-
nodes
570-
.into_iter()
571-
.flat_map(|(account_hash, nodes)| {
572-
nodes
573-
.into_iter()
574-
.map(move |(path, node)| (apply_prefix(Some(account_hash), path), node))
575-
})
576-
.chain(state_nodes)
577-
.collect(),
578-
)?;
579-
Ok(state_root)
571+
572+
Ok(account_trie.hash()?)
580573
}
581574

582575
pub async fn add_receipt(

crates/storage/store_db/rocksdb.rs

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,15 @@ const CF_CHAIN_DATA: &str = "chain_data";
9595
/// - [`Vec<u8>`] = `BlockHashRLP::from(block_hash).bytes().clone()`
9696
const CF_SNAP_STATE: &str = "snap_state";
9797

98-
/// State trie nodes column family: [`Nibbles`] => [`Vec<u8>`]
98+
/// Account State trie nodes column family: [`Nibbles`] => [`Vec<u8>`]
9999
/// - [`Nibbles`] = `node_hash.as_ref()`
100100
/// - [`Vec<u8>`] = `node_data`
101-
const CF_TRIE_NODES: &str = "trie_nodes";
101+
const CF_ACCOUNT_TRIE_NODES: &str = "account_trie_nodes";
102+
103+
/// Storage trie nodes column family: [`Nibbles`] => [`Vec<u8>`]
104+
/// - [`Nibbles`] = `node_hash.as_ref()`
105+
/// - [`Vec<u8>`] = `node_data`
106+
const CF_STORAGE_TRIE_NODES: &str = "storage_trie_nodes";
102107

103108
/// Pending blocks column family: [`Vec<u8>`] => [`Vec<u8>`]
104109
/// - [`Vec<u8>`] = `BlockHashRLP::from(block.hash()).bytes().clone()`
@@ -115,7 +120,15 @@ const CF_INVALID_ANCESTORS: &str = "invalid_ancestors";
115120
/// - [`Vec<u8>`] = `BlockHeaderRLP::from(block.header.clone()).bytes().clone()`
116121
const CF_FULLSYNC_HEADERS: &str = "fullsync_headers";
117122

118-
pub const CF_FLATKEYVALUE: &str = "flatkeyvalue";
123+
/// Account sate flat key-value store: [`Nibbles`] => [`Vec<u8>`]
124+
/// - [`Nibbles`] = `node_hash.as_ref()`
125+
/// - [`Vec<u8>`] = `node_data`
126+
pub const CF_ACCOUNT_FLATKEYVALUE: &str = "account_flatkeyvalue";
127+
128+
/// Storage slots key-value store: [`Nibbles`] => [`Vec<u8>`]
129+
/// - [`Nibbles`] = `node_hash.as_ref()`
130+
/// - [`Vec<u8>`] = `node_data`
131+
pub const CF_STORAGE_FLATKEYVALUE: &str = "storage_flatkeyvalue";
119132

120133
pub const CF_MISC_VALUES: &str = "misc_values";
121134

@@ -197,11 +210,13 @@ impl Store {
197210
CF_TRANSACTION_LOCATIONS,
198211
CF_CHAIN_DATA,
199212
CF_SNAP_STATE,
200-
CF_TRIE_NODES,
213+
CF_ACCOUNT_TRIE_NODES,
214+
CF_STORAGE_TRIE_NODES,
201215
CF_PENDING_BLOCKS,
202216
CF_INVALID_ANCESTORS,
203217
CF_FULLSYNC_HEADERS,
204-
CF_FLATKEYVALUE,
218+
CF_ACCOUNT_FLATKEYVALUE,
219+
CF_STORAGE_FLATKEYVALUE,
205220
CF_MISC_VALUES,
206221
];
207222

@@ -263,7 +278,7 @@ impl Store {
263278
block_opts.set_bloom_filter(10.0, false);
264279
cf_opts.set_block_based_table_factory(&block_opts);
265280
}
266-
CF_TRIE_NODES => {
281+
CF_ACCOUNT_TRIE_NODES | CF_STORAGE_TRIE_NODES => {
267282
cf_opts.set_write_buffer_size(512 * 1024 * 1024); // 512MB
268283
cf_opts.set_max_write_buffer_number(6);
269284
cf_opts.set_min_write_buffer_number_to_merge(2);
@@ -275,7 +290,7 @@ impl Store {
275290
block_opts.set_bloom_filter(10.0, false); // 10 bits per key
276291
cf_opts.set_block_based_table_factory(&block_opts);
277292
}
278-
CF_FLATKEYVALUE => {
293+
CF_ACCOUNT_FLATKEYVALUE | CF_STORAGE_FLATKEYVALUE => {
279294
cf_opts.set_write_buffer_size(512 * 1024 * 1024); // 512MB
280295
cf_opts.set_max_write_buffer_number(6);
281296
cf_opts.set_min_write_buffer_number_to_merge(2);
@@ -579,7 +594,8 @@ impl Store {
579594
control_rx: &mut std::sync::mpsc::Receiver<FKVGeneratorControlMessage>,
580595
) -> Result<(), StoreError> {
581596
let cf_misc = self.cf_handle(CF_MISC_VALUES)?;
582-
let cf_flatkeyvalue = self.cf_handle(CF_FLATKEYVALUE)?;
597+
let cf_accounts_fkv = self.cf_handle(CF_ACCOUNT_FLATKEYVALUE)?;
598+
let cf_storage_fkv = self.cf_handle(CF_STORAGE_FLATKEYVALUE)?;
583599

584600
let last_written = self
585601
.db
@@ -590,11 +606,13 @@ impl Store {
590606
}
591607

592608
self.db
593-
.delete_range_cf(&cf_flatkeyvalue, last_written, vec![0xff])?;
609+
.delete_range_cf(&cf_accounts_fkv, &last_written, vec![0xff].as_ref())?;
610+
self.db
611+
.delete_range_cf(&cf_storage_fkv, &last_written, vec![0xff].as_ref())?;
594612

595613
loop {
596614
let root = self
597-
.read_sync(CF_TRIE_NODES, [])?
615+
.read_sync(CF_ACCOUNT_TRIE_NODES, [])?
598616
.ok_or(StoreError::MissingLatestBlockNumber)?;
599617
let root: Node = ethrex_trie::Node::decode(&root)?;
600618
let state_root = root.compute_hash().finalize();
@@ -616,18 +634,18 @@ impl Store {
616634

617635
let mut ctr = 0;
618636
let mut batch = WriteBatch::default();
619-
let mut iter = self.open_direct_state_trie(state_root)?.into_iter();
637+
let mut account_iter = self.open_direct_state_trie(state_root)?.into_iter();
620638
if last_written_account > Nibbles::default() {
621-
iter.advance(last_written_account.to_bytes())?;
639+
account_iter.advance(last_written_account.to_bytes())?;
622640
}
623-
let res = iter.try_for_each(|(path, node)| -> Result<(), StoreError> {
624-
let Node::Leaf(node) = node else {
641+
let res = account_iter.try_for_each(|(path, account_node)| -> Result<(), StoreError> {
642+
let Node::Leaf(node) = account_node else {
625643
return Ok(());
626644
};
627645
let account_state = AccountState::decode(&node.value)?;
628646
let account_hash = H256::from_slice(&path.to_bytes());
629647
batch.put_cf(&cf_misc, "last_written", path.as_ref());
630-
batch.put_cf(&cf_flatkeyvalue, path.as_ref(), node.value);
648+
batch.put_cf(&cf_accounts_fkv, path.as_ref(), node.value);
631649
ctr += 1;
632650
if ctr > 10_000 {
633651
self.db.write(std::mem::take(&mut batch))?;
@@ -638,20 +656,20 @@ impl Store {
638656
ctr = 0;
639657
}
640658

641-
let mut iter_inner = self
659+
let mut storage_iter = self
642660
.open_direct_storage_trie(account_hash, account_state.storage_root)?
643661
.into_iter();
644662
if last_written_storage > Nibbles::default() {
645-
iter_inner.advance(last_written_storage.to_bytes())?;
663+
storage_iter.advance(last_written_storage.to_bytes())?;
646664
last_written_storage = Nibbles::default();
647665
}
648-
iter_inner.try_for_each(|(path, node)| -> Result<(), StoreError> {
649-
let Node::Leaf(node) = node else {
666+
storage_iter.try_for_each(|(path, storage_node)| -> Result<(), StoreError> {
667+
let Node::Leaf(node) = storage_node else {
650668
return Ok(());
651669
};
652670
let key = apply_prefix(Some(account_hash), path);
653671
batch.put_cf(&cf_misc, "last_written", key.as_ref());
654-
batch.put_cf(&cf_flatkeyvalue, key.as_ref(), node.value);
672+
batch.put_cf(&cf_storage_fkv, key.as_ref(), node.value);
655673
ctr += 1;
656674
if ctr > 10_000 {
657675
self.db.write(std::mem::take(&mut batch))?;
@@ -754,22 +772,47 @@ impl Store {
754772
// RCU to remove the bottom layer: update step needs to happen after disk layer is updated.
755773
let mut trie_mut = (*trie).clone();
756774
let mut batch = WriteBatch::default();
757-
let [cf_trie_nodes, cf_flatkeyvalue, cf_misc] =
758-
open_cfs(db, [CF_TRIE_NODES, CF_FLATKEYVALUE, CF_MISC_VALUES])?;
775+
let [
776+
cf_accounts_trie_nodes,
777+
cf_accounts_flatkeyvalue,
778+
cf_storage_trie_nodes,
779+
cf_storage_flatkeyvalue,
780+
cf_misc,
781+
] = open_cfs(
782+
db,
783+
[
784+
CF_ACCOUNT_TRIE_NODES,
785+
CF_ACCOUNT_FLATKEYVALUE,
786+
CF_STORAGE_TRIE_NODES,
787+
CF_STORAGE_FLATKEYVALUE,
788+
CF_MISC_VALUES,
789+
],
790+
)?;
759791

760792
let last_written = db.get_cf(&cf_misc, "last_written")?.unwrap_or_default();
793+
794+
// Before encoding, accounts have only the account address as their path, while storage keys have
795+
// the account address (32 bytes) + storage path (up to 32 bytes).
796+
761797
// Commit removes the bottom layer and returns it, this is the mutation step.
762798
let nodes = trie_mut.commit(root).unwrap_or_default();
763799
for (key, value) in nodes {
764800
let is_leaf = key.len() == 65 || key.len() == 131;
801+
let is_account = key.len() <= 65;
765802

766803
if is_leaf && key > last_written {
767804
continue;
768805
}
769806
let cf = if is_leaf {
770-
&cf_flatkeyvalue
807+
if is_account {
808+
&cf_accounts_flatkeyvalue
809+
} else {
810+
&cf_storage_flatkeyvalue
811+
}
812+
} else if is_account {
813+
&cf_accounts_trie_nodes
771814
} else {
772-
&cf_trie_nodes
815+
&cf_storage_trie_nodes
773816
};
774817
if value.is_empty() {
775818
batch.delete_cf(cf, key);
@@ -836,6 +879,7 @@ impl StoreEngine for Store {
836879
CF_BODIES,
837880
],
838881
)?;
882+
839883
let mut batch = WriteBatch::default();
840884

841885
let UpdateBatch {
@@ -1482,7 +1526,8 @@ impl StoreEngine for Store {
14821526
// FIXME: use a DB snapshot here
14831527
let db = Box::new(RocksDBTrieDB::new(
14841528
self.db.clone(),
1485-
CF_TRIE_NODES,
1529+
CF_STORAGE_TRIE_NODES,
1530+
CF_STORAGE_FLATKEYVALUE,
14861531
None,
14871532
self.last_written()?,
14881533
)?);
@@ -1503,7 +1548,8 @@ impl StoreEngine for Store {
15031548
// FIXME: use a DB snapshot here
15041549
let db = Box::new(RocksDBTrieDB::new(
15051550
self.db.clone(),
1506-
CF_TRIE_NODES,
1551+
CF_ACCOUNT_TRIE_NODES,
1552+
CF_ACCOUNT_FLATKEYVALUE,
15071553
None,
15081554
self.last_written()?,
15091555
)?);
@@ -1527,7 +1573,8 @@ impl StoreEngine for Store {
15271573
) -> Result<Trie, StoreError> {
15281574
let db = Box::new(RocksDBTrieDB::new(
15291575
self.db.clone(),
1530-
CF_TRIE_NODES,
1576+
CF_STORAGE_TRIE_NODES,
1577+
CF_STORAGE_FLATKEYVALUE,
15311578
Some(hashed_address),
15321579
self.last_written()?,
15331580
)?);
@@ -1537,7 +1584,8 @@ impl StoreEngine for Store {
15371584
fn open_direct_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
15381585
let db = Box::new(RocksDBTrieDB::new(
15391586
self.db.clone(),
1540-
CF_TRIE_NODES,
1587+
CF_ACCOUNT_TRIE_NODES,
1588+
CF_ACCOUNT_FLATKEYVALUE,
15411589
None,
15421590
self.last_written()?,
15431591
)?);
@@ -1547,7 +1595,8 @@ impl StoreEngine for Store {
15471595
fn open_locked_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
15481596
let db = Box::new(RocksDBLockedTrieDB::new(
15491597
self.db.clone(),
1550-
CF_TRIE_NODES,
1598+
CF_ACCOUNT_TRIE_NODES,
1599+
CF_ACCOUNT_FLATKEYVALUE,
15511600
None,
15521601
self.last_written()?,
15531602
)?);
@@ -1572,7 +1621,8 @@ impl StoreEngine for Store {
15721621
) -> Result<Trie, StoreError> {
15731622
let db = Box::new(RocksDBLockedTrieDB::new(
15741623
self.db.clone(),
1575-
CF_TRIE_NODES,
1624+
CF_STORAGE_TRIE_NODES,
1625+
CF_STORAGE_FLATKEYVALUE,
15761626
None,
15771627
self.last_written()?,
15781628
)?);
@@ -1848,8 +1898,8 @@ impl StoreEngine for Store {
18481898
let db = self.db.clone();
18491899
tokio::task::spawn_blocking(move || {
18501900
let mut batch = WriteBatch::default();
1851-
let cf = db.cf_handle(CF_TRIE_NODES).ok_or_else(|| {
1852-
StoreError::Custom("Column family not found: CF_TRIE_NODES".to_string())
1901+
let cf = db.cf_handle(CF_STORAGE_TRIE_NODES).ok_or_else(|| {
1902+
StoreError::Custom("Column family not found: CF_STORAGE_TRIE_NODES".to_string())
18531903
})?;
18541904

18551905
for (address_hash, nodes) in storage_trie_nodes {

0 commit comments

Comments
 (0)