Skip to content

Commit dad1d71

Browse files
authored
Merge af578b2 into f09eb24
2 parents f09eb24 + af578b2 commit dad1d71

40 files changed

Lines changed: 1217 additions & 636 deletions

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/ethrex/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ ethrex-l2.workspace = true
2222
ethrex-l2-common.workspace = true
2323
ethrex-sdk.workspace = true
2424
ethrex-l2-rpc.workspace = true
25+
ethrex-trie.workspace = true
2526
tikv-jemallocator = { version = "0.6.0", optional = true, features = ["stats", "unprefixed_malloc_on_supported_platforms"] }
2627
bytes.workspace = true
2728
hex.workspace = true

cmd/ethrex/initializers.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use ethrex_p2p::{
2121
types::{Node, NodeRecord},
2222
utils::public_key_from_signing_key,
2323
};
24+
use ethrex_rlp::decode::RLPDecode;
2425
use ethrex_storage::{EngineType, Store};
26+
use ethrex_trie::EMPTY_TRIE_HASH;
2527
use local_ip_address::{local_ip, local_ipv6};
2628
use rand::rngs::OsRng;
2729
use secp256k1::SecretKey;
@@ -385,6 +387,44 @@ async fn set_sync_block(store: &Store) {
385387
}
386388
}
387389

390+
/// Rollback the chain to the latest block we have the state for.
391+
async fn reset_to_head(store: &Store) -> eyre::Result<()> {
392+
let trie = store.open_direct_state_trie(*EMPTY_TRIE_HASH)?;
393+
let Some(root) = trie.db().get(Default::default())? else {
394+
return Ok(());
395+
};
396+
let root = ethrex_trie::Node::decode(&root)?;
397+
let state_root = root.compute_hash().finalize();
398+
399+
// TODO: store latest state metadata in the DB to avoid this loop
400+
for block_number in (0..=store.get_latest_block_number().await?).rev() {
401+
if let Some(header) = store.get_block_header(block_number)?
402+
&& header.state_root == state_root
403+
{
404+
info!("Resetting head to {block_number}");
405+
let last_kept_block = block_number;
406+
let mut block_to_delete = last_kept_block + 1;
407+
while store
408+
.get_canonical_block_hash(block_to_delete)
409+
.await?
410+
.is_some()
411+
{
412+
debug!("Deleting block {block_to_delete}");
413+
store.remove_block(block_to_delete).await?;
414+
block_to_delete += 1;
415+
}
416+
let last_kept_header = store
417+
.get_block_header(last_kept_block)?
418+
.ok_or_else(|| eyre::eyre!("Block number {} not found", last_kept_block))?;
419+
store
420+
.forkchoice_update(None, last_kept_block, last_kept_header.hash(), None, None)
421+
.await?;
422+
break;
423+
}
424+
}
425+
Ok(())
426+
}
427+
388428
pub async fn init_l1(
389429
opts: Options,
390430
log_filter_handler: Option<reload::Handle<EnvFilter, Registry>>,
@@ -406,6 +446,8 @@ pub async fn init_l1(
406446

407447
let store = init_store(datadir, genesis).await;
408448

449+
reset_to_head(&store).await?;
450+
409451
#[cfg(feature = "sync-test")]
410452
set_sync_block(&store).await;
411453

cmd/ethrex/l2/command.rs

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use ethrex_l2_sdk::call_contract;
2020
use ethrex_rpc::{
2121
EthClient, clients::beacon::BeaconClient, types::block_identifier::BlockIdentifier,
2222
};
23-
use ethrex_storage::{EngineType, Store, UpdateBatch};
23+
use ethrex_storage::{EngineType, Store};
2424
use ethrex_storage_rollup::StoreRollup;
2525
use eyre::OptionExt;
2626
use itertools::Itertools;
@@ -405,11 +405,8 @@ impl Command {
405405

406406
// Get genesis
407407
let genesis_header = store.get_block_header(0)?.expect("Genesis block not found");
408-
let genesis_block_hash = genesis_header.hash();
409408

410-
let mut new_trie = store
411-
.state_trie(genesis_block_hash)?
412-
.expect("Genesis block not found");
409+
let mut current_state_root = genesis_header.state_root;
413410

414411
let mut last_block_number = 0;
415412
let mut new_canonical_blocks = vec![];
@@ -433,37 +430,32 @@ impl Command {
433430
let state_diff = StateDiff::decode(&blob)?;
434431

435432
// Apply all account updates to trie
436-
let account_updates = state_diff.to_account_updates(&new_trie)?;
433+
let trie = store.open_direct_state_trie(current_state_root)?;
434+
435+
let account_updates = state_diff.to_account_updates(&trie)?;
436+
437+
dbg!(&account_updates);
438+
437439
let account_updates_list = store
438-
.apply_account_updates_from_trie_batch(new_trie, account_updates.values())
440+
.apply_account_updates_from_trie_batch(trie, account_updates.values())
439441
.await
440442
.map_err(|e| format!("Error applying account updates: {e}"))
441443
.unwrap();
442444

443-
let (new_state_root, state_updates, accounts_updates) = (
444-
account_updates_list.state_trie_hash,
445-
account_updates_list.state_updates,
446-
account_updates_list.storage_updates,
447-
);
445+
store
446+
.open_direct_state_trie(current_state_root)?
447+
.db()
448+
.put_batch(account_updates_list.state_updates)?;
448449

449-
let pseudo_update_batch = UpdateBatch {
450-
account_updates: state_updates,
451-
storage_updates: accounts_updates,
452-
blocks: vec![],
453-
receipts: vec![],
454-
code_updates: vec![],
455-
};
450+
current_state_root = account_updates_list.state_trie_hash;
456451

457452
store
458-
.store_block_updates(pseudo_update_batch)
459-
.await
460-
.map_err(|e| format!("Error storing trie updates: {e}"))
461-
.unwrap();
453+
.write_storage_trie_nodes_batch(account_updates_list.storage_updates)
454+
.await?;
462455

463-
new_trie = store
464-
.open_state_trie(new_state_root)
465-
.map_err(|e| format!("Error opening new state trie: {e}"))
466-
.unwrap();
456+
store
457+
.write_account_code_batch(account_updates_list.code_updates)
458+
.await?;
467459

468460
// Get withdrawal hashes
469461
let message_hashes = state_diff
@@ -479,10 +471,7 @@ impl Command {
479471
// Note that its state_root is the root of new_trie.
480472
let new_block = BlockHeader {
481473
coinbase,
482-
state_root: new_trie
483-
.hash()
484-
.map_err(|e| format!("Error committing state: {e}"))
485-
.unwrap(),
474+
state_root: account_updates_list.state_trie_hash,
486475
..state_diff.last_header
487476
};
488477

crates/blockchain/blockchain.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ impl Blockchain {
416416
};
417417

418418
self.storage
419-
.clone()
420419
.store_block_updates(update_batch)
421420
.await
422421
.map_err(|e| e.into())

crates/blockchain/smoke_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ mod blockchain_integration_test {
310310
};
311311

312312
// Create blockchain
313-
let blockchain = Blockchain::default_with_store(store.clone().clone());
313+
let blockchain = Blockchain::default_with_store(store.clone());
314314

315315
let block = create_payload(&args, store, Bytes::new()).unwrap();
316316
let result = blockchain.build_payload(block).await.unwrap();

crates/blockchain/tracing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ async fn get_missing_state_parents(
135135
let Some(parent_block) = store.get_block_by_hash(parent_hash).await? else {
136136
return Err(ChainError::Custom("Parent Block not Found".to_string()));
137137
};
138-
if store.contains_state_node(parent_block.header.state_root)? {
138+
if store.has_state_root(parent_block.header.state_root)? {
139139
break;
140140
}
141141
parent_hash = parent_block.header.parent_hash;

crates/common/trie/db.rs

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,58 @@
11
use ethereum_types::H256;
22
use ethrex_rlp::encode::RLPEncode;
33

4-
use crate::{Node, NodeHash, NodeRLP, Trie, error::TrieError};
4+
use crate::{Nibbles, Node, NodeRLP, Trie, error::TrieError};
55
use std::{
66
collections::BTreeMap,
77
sync::{Arc, Mutex},
88
};
99

10+
// Nibbles -> encoded node
11+
pub type NodeMap = Arc<Mutex<BTreeMap<Vec<u8>, Vec<u8>>>>;
12+
1013
pub trait TrieDB: Send + Sync {
11-
fn get(&self, key: NodeHash) -> Result<Option<Vec<u8>>, TrieError>;
12-
fn put_batch(&self, key_values: Vec<(NodeHash, Vec<u8>)>) -> Result<(), TrieError>;
14+
fn get(&self, key: Nibbles) -> Result<Option<Vec<u8>>, TrieError>;
15+
fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError>;
1316
// TODO: replace putbatch with this function.
14-
fn put_batch_no_alloc(&self, key_values: &[(NodeHash, Node)]) -> Result<(), TrieError> {
17+
fn put_batch_no_alloc(&self, key_values: &[(Nibbles, Node)]) -> Result<(), TrieError> {
1518
self.put_batch(
1619
key_values
1720
.iter()
18-
.map(|node| (node.0, node.1.encode_to_vec()))
21+
.map(|node| (node.0.clone(), node.1.encode_to_vec()))
1922
.collect(),
2023
)
2124
}
22-
fn put(&self, key: NodeHash, value: Vec<u8>) -> Result<(), TrieError> {
25+
fn put(&self, key: Nibbles, value: Vec<u8>) -> Result<(), TrieError> {
2326
self.put_batch(vec![(key, value)])
2427
}
2528
}
2629

2730
/// InMemory implementation for the TrieDB trait, with get and put operations.
2831
#[derive(Default)]
2932
pub struct InMemoryTrieDB {
30-
pub inner: Arc<Mutex<BTreeMap<NodeHash, Vec<u8>>>>,
33+
inner: NodeMap,
34+
prefix: Option<Nibbles>,
3135
}
3236

3337
impl InMemoryTrieDB {
34-
pub const fn new(map: Arc<Mutex<BTreeMap<NodeHash, Vec<u8>>>>) -> Self {
35-
Self { inner: map }
38+
pub const fn new(map: NodeMap) -> Self {
39+
Self {
40+
inner: map,
41+
prefix: None,
42+
}
43+
}
44+
45+
pub const fn new_with_prefix(map: NodeMap, prefix: Nibbles) -> Self {
46+
Self {
47+
inner: map,
48+
prefix: Some(prefix),
49+
}
3650
}
51+
3752
pub fn new_empty() -> Self {
3853
Self {
3954
inner: Default::default(),
55+
prefix: None,
4056
}
4157
}
4258

@@ -45,33 +61,56 @@ impl InMemoryTrieDB {
4561
state_nodes: &BTreeMap<H256, NodeRLP>,
4662
) -> Result<Self, TrieError> {
4763
let mut embedded_root = Trie::get_embedded_root(state_nodes, root_hash)?;
48-
let mut hashed_nodes: Vec<(NodeHash, Vec<u8>)> = vec![];
49-
embedded_root.commit(&mut hashed_nodes);
64+
let mut hashed_nodes = vec![];
65+
embedded_root.commit(Nibbles::default(), &mut hashed_nodes);
5066

51-
let hashed_nodes = hashed_nodes.into_iter().collect();
67+
let hashed_nodes = hashed_nodes
68+
.into_iter()
69+
.map(|(k, v)| (k.into_vec(), v))
70+
.collect();
5271

5372
let in_memory_trie = Arc::new(Mutex::new(hashed_nodes));
5473
Ok(Self::new(in_memory_trie))
5574
}
75+
76+
fn apply_prefix(&self, path: Nibbles) -> Nibbles {
77+
match &self.prefix {
78+
Some(prefix) => prefix.concat(&path),
79+
None => path,
80+
}
81+
}
5682
}
5783

5884
impl TrieDB for InMemoryTrieDB {
59-
fn get(&self, key: NodeHash) -> Result<Option<Vec<u8>>, TrieError> {
85+
fn get(&self, key: Nibbles) -> Result<Option<Vec<u8>>, TrieError> {
6086
Ok(self
6187
.inner
6288
.lock()
6389
.map_err(|_| TrieError::LockError)?
64-
.get(&key)
90+
.get(self.apply_prefix(key).as_ref())
6591
.cloned())
6692
}
6793

68-
fn put_batch(&self, key_values: Vec<(NodeHash, Vec<u8>)>) -> Result<(), TrieError> {
94+
fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError> {
6995
let mut db = self.inner.lock().map_err(|_| TrieError::LockError)?;
7096

7197
for (key, value) in key_values {
72-
db.insert(key, value);
98+
let prefixed_key = self.apply_prefix(key);
99+
db.insert(prefixed_key.into_vec(), value);
73100
}
74101

75102
Ok(())
76103
}
77104
}
105+
106+
pub fn nibbles_to_fixed_size(nibbles: Nibbles) -> [u8; 33] {
107+
let node_hash_ref = nibbles.to_bytes();
108+
let original_len = node_hash_ref.len();
109+
110+
let mut buffer = [0u8; 33];
111+
112+
// Encode the node as [node_path..., original_len]
113+
buffer[32] = nibbles.len() as u8;
114+
buffer[..original_len].copy_from_slice(&node_hash_ref);
115+
buffer
116+
}

crates/common/trie/logger.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55

66
use ethrex_rlp::decode::RLPDecode;
77

8-
use crate::{Node, NodeHash, Trie, TrieDB, TrieError};
8+
use crate::{Nibbles, Node, Trie, TrieDB, TrieError};
99

1010
pub type TrieWitness = Arc<Mutex<HashSet<Vec<u8>>>>;
1111

@@ -33,7 +33,7 @@ impl TrieLogger {
3333
}
3434

3535
impl TrieDB for TrieLogger {
36-
fn get(&self, key: NodeHash) -> Result<Option<Vec<u8>>, TrieError> {
36+
fn get(&self, key: Nibbles) -> Result<Option<Vec<u8>>, TrieError> {
3737
let result = self.inner_db.get(key)?;
3838
if let Some(result) = result.as_ref()
3939
&& let Ok(decoded) = Node::decode(result)
@@ -44,11 +44,11 @@ impl TrieDB for TrieLogger {
4444
Ok(result)
4545
}
4646

47-
fn put(&self, key: NodeHash, value: Vec<u8>) -> Result<(), TrieError> {
47+
fn put(&self, key: Nibbles, value: Vec<u8>) -> Result<(), TrieError> {
4848
self.inner_db.put(key, value)
4949
}
5050

51-
fn put_batch(&self, key_values: Vec<(NodeHash, Vec<u8>)>) -> Result<(), TrieError> {
51+
fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError> {
5252
self.inner_db.put_batch(key_values)
5353
}
5454
}

0 commit comments

Comments
 (0)