Skip to content

Commit 4b1a0ce

Browse files
authored
refactor(trie): trie node iterators (#5048)
1 parent 9d9de58 commit 4b1a0ce

File tree

14 files changed

+413
-280
lines changed

14 files changed

+413
-280
lines changed

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.

crates/primitives/src/stage/checkpoints.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct MerkleCheckpoint {
1717
pub target_block: BlockNumber,
1818
/// The last hashed account key processed.
1919
pub last_account_key: B256,
20+
// TODO: remove in the next breaking release.
2021
/// The last walker key processed.
2122
pub last_walker_key: Vec<u8>,
2223
/// Previously recorded walker stack.
@@ -30,11 +31,16 @@ impl MerkleCheckpoint {
3031
pub fn new(
3132
target_block: BlockNumber,
3233
last_account_key: B256,
33-
last_walker_key: Vec<u8>,
3434
walker_stack: Vec<StoredSubNode>,
3535
state: HashBuilderState,
3636
) -> Self {
37-
Self { target_block, last_account_key, last_walker_key, walker_stack, state }
37+
Self {
38+
target_block,
39+
last_account_key,
40+
walker_stack,
41+
state,
42+
last_walker_key: Vec::default(),
43+
}
3844
}
3945
}
4046

crates/stages/src/stages/merkle.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ impl<DB: Database> Stage<DB> for MerkleStage {
224224
let checkpoint = MerkleCheckpoint::new(
225225
to_block,
226226
state.last_account_key,
227-
state.last_walker_key.hex_data.to_vec(),
228227
state.walker_stack.into_iter().map(StoredSubNode::from).collect(),
229228
state.hash_builder.into(),
230229
);

crates/trie/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tracing.workspace = true
2727
# misc
2828
thiserror.workspace = true
2929
derive_more = "0.99"
30+
auto_impl = "1"
3031

3132
# test-utils
3233
triehash = { version = "0.8", optional = true }

crates/trie/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ pub mod walker;
3434
mod errors;
3535
pub use errors::*;
3636

37+
// The iterators for traversing existing intermediate hashes and updated trie leaves.
38+
pub(crate) mod node_iter;
39+
3740
/// Merkle proof generation.
3841
pub mod proof;
3942

crates/trie/src/node_iter.rs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
use crate::{
2+
hashed_cursor::{HashedAccountCursor, HashedStorageCursor},
3+
trie_cursor::TrieCursor,
4+
walker::TrieWalker,
5+
StateRootError, StorageRootError,
6+
};
7+
use reth_primitives::{trie::Nibbles, Account, StorageEntry, B256, U256};
8+
9+
#[derive(Debug)]
10+
pub(crate) struct TrieBranchNode {
11+
pub(crate) key: Nibbles,
12+
pub(crate) value: B256,
13+
pub(crate) children_are_in_trie: bool,
14+
}
15+
16+
impl TrieBranchNode {
17+
pub(crate) fn new(key: Nibbles, value: B256, children_are_in_trie: bool) -> Self {
18+
Self { key, value, children_are_in_trie }
19+
}
20+
}
21+
22+
#[derive(Debug)]
23+
pub(crate) enum AccountNode {
24+
Branch(TrieBranchNode),
25+
Leaf(B256, Account),
26+
}
27+
28+
#[derive(Debug)]
29+
pub(crate) enum StorageNode {
30+
Branch(TrieBranchNode),
31+
Leaf(B256, U256),
32+
}
33+
34+
/// An iterator over existing intermediate branch nodes and updated leaf nodes.
35+
#[derive(Debug)]
36+
pub(crate) struct AccountNodeIter<C, H> {
37+
/// Underlying walker over intermediate nodes.
38+
pub(crate) walker: TrieWalker<C>,
39+
/// The cursor for the hashed account entries.
40+
pub(crate) hashed_account_cursor: H,
41+
/// The previous account key. If the iteration was previously interrupted, this value can be
42+
/// used to resume iterating from the last returned leaf node.
43+
previous_account_key: Option<B256>,
44+
45+
/// Current hashed account entry.
46+
current_hashed_entry: Option<(B256, Account)>,
47+
/// Flag indicating whether we should check the current walker key.
48+
current_walker_key_checked: bool,
49+
}
50+
51+
impl<C, H> AccountNodeIter<C, H> {
52+
pub(crate) fn new(walker: TrieWalker<C>, hashed_account_cursor: H) -> Self {
53+
Self {
54+
walker,
55+
hashed_account_cursor,
56+
previous_account_key: None,
57+
current_hashed_entry: None,
58+
current_walker_key_checked: false,
59+
}
60+
}
61+
62+
pub(crate) fn with_last_account_key(mut self, previous_account_key: B256) -> Self {
63+
self.previous_account_key = Some(previous_account_key);
64+
self
65+
}
66+
}
67+
68+
impl<C, H> AccountNodeIter<C, H>
69+
where
70+
C: TrieCursor,
71+
H: HashedAccountCursor,
72+
{
73+
/// Return the next account trie node to be added to the hash builder.
74+
///
75+
/// Returns the nodes using this algorithm:
76+
/// 1. Return the current intermediate branch node if it hasn't been updated.
77+
/// 2. Advance the trie walker to the next intermediate branch node and retrieve next
78+
/// unprocessed key.
79+
/// 3. Reposition the hashed account cursor on the next unprocessed key.
80+
/// 4. Return every hashed account entry up to the key of the current intermediate branch node.
81+
/// 5. Repeat.
82+
///
83+
/// NOTE: The iteration will start from the key of the previous hashed entry if it was supplied.
84+
pub(crate) fn try_next(&mut self) -> Result<Option<AccountNode>, StateRootError> {
85+
loop {
86+
if let Some(key) = self.walker.key() {
87+
if !self.current_walker_key_checked && self.previous_account_key.is_none() {
88+
self.current_walker_key_checked = true;
89+
if self.walker.can_skip_current_node {
90+
return Ok(Some(AccountNode::Branch(TrieBranchNode::new(
91+
key,
92+
self.walker.hash().unwrap(),
93+
self.walker.children_are_in_trie(),
94+
))))
95+
}
96+
}
97+
}
98+
99+
if let Some((hashed_address, account)) = self.current_hashed_entry.take() {
100+
if self.walker.key().map_or(false, |key| key < Nibbles::unpack(hashed_address)) {
101+
self.current_walker_key_checked = false;
102+
continue
103+
}
104+
105+
self.current_hashed_entry = self.hashed_account_cursor.next()?;
106+
return Ok(Some(AccountNode::Leaf(hashed_address, account)))
107+
}
108+
109+
match self.previous_account_key.take() {
110+
Some(account_key) => {
111+
self.hashed_account_cursor.seek(account_key)?;
112+
self.current_hashed_entry = self.hashed_account_cursor.next()?;
113+
}
114+
None => {
115+
let seek_key = match self.walker.next_unprocessed_key() {
116+
Some(key) => key,
117+
None => break, // no more keys
118+
};
119+
self.current_hashed_entry = self.hashed_account_cursor.seek(seek_key)?;
120+
self.walker.advance()?;
121+
}
122+
}
123+
}
124+
125+
Ok(None)
126+
}
127+
}
128+
129+
#[derive(Debug)]
130+
pub(crate) struct StorageNodeIter<C, H> {
131+
/// Underlying walker over intermediate nodes.
132+
pub(crate) walker: TrieWalker<C>,
133+
/// The cursor for the hashed storage entries.
134+
pub(crate) hashed_storage_cursor: H,
135+
/// The hashed address this storage trie belongs to.
136+
hashed_address: B256,
137+
138+
/// Current hashed storage entry.
139+
current_hashed_entry: Option<StorageEntry>,
140+
/// Flag indicating whether we should check the current walker key.
141+
current_walker_key_checked: bool,
142+
}
143+
144+
impl<C, H> StorageNodeIter<C, H> {
145+
pub(crate) fn new(
146+
walker: TrieWalker<C>,
147+
hashed_storage_cursor: H,
148+
hashed_address: B256,
149+
) -> Self {
150+
Self {
151+
walker,
152+
hashed_storage_cursor,
153+
hashed_address,
154+
current_walker_key_checked: false,
155+
current_hashed_entry: None,
156+
}
157+
}
158+
}
159+
160+
impl<C, H> StorageNodeIter<C, H>
161+
where
162+
C: TrieCursor,
163+
H: HashedStorageCursor,
164+
{
165+
/// Return the next storage trie node to be added to the hash builder.
166+
///
167+
/// Returns the nodes using this algorithm:
168+
/// 1. Return the current intermediate branch node if it hasn't been updated.
169+
/// 2. Advance the trie walker to the next intermediate branch node and retrieve next
170+
/// unprocessed key.
171+
/// 3. Reposition the hashed storage cursor on the next unprocessed key.
172+
/// 4. Return every hashed storage entry up to the key of the current intermediate branch node.
173+
/// 5. Repeat.
174+
pub(crate) fn try_next(&mut self) -> Result<Option<StorageNode>, StorageRootError> {
175+
loop {
176+
if let Some(key) = self.walker.key() {
177+
if !self.current_walker_key_checked {
178+
self.current_walker_key_checked = true;
179+
if self.walker.can_skip_current_node {
180+
return Ok(Some(StorageNode::Branch(TrieBranchNode::new(
181+
key,
182+
self.walker.hash().unwrap(),
183+
self.walker.children_are_in_trie(),
184+
))))
185+
}
186+
}
187+
}
188+
189+
if let Some(StorageEntry { key: hashed_key, value }) = self.current_hashed_entry.take()
190+
{
191+
if self.walker.key().map_or(false, |key| key < Nibbles::unpack(hashed_key)) {
192+
self.current_walker_key_checked = false;
193+
continue
194+
}
195+
196+
self.current_hashed_entry = self.hashed_storage_cursor.next()?;
197+
return Ok(Some(StorageNode::Leaf(hashed_key, value)))
198+
}
199+
200+
let Some(seek_key) = self.walker.next_unprocessed_key() else { break };
201+
self.current_hashed_entry =
202+
self.hashed_storage_cursor.seek(self.hashed_address, seek_key)?;
203+
self.walker.advance()?;
204+
}
205+
206+
Ok(None)
207+
}
208+
}

crates/trie/src/progress.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
use crate::{trie_cursor::CursorSubNode, updates::TrieUpdates};
2-
use reth_primitives::{
3-
stage::MerkleCheckpoint,
4-
trie::{hash_builder::HashBuilder, Nibbles},
5-
B256,
6-
};
2+
use reth_primitives::{stage::MerkleCheckpoint, trie::hash_builder::HashBuilder, B256};
73

84
/// The progress of the state root computation.
95
#[derive(Debug)]
@@ -24,8 +20,6 @@ pub struct IntermediateStateRootState {
2420
pub walker_stack: Vec<CursorSubNode>,
2521
/// The last hashed account key processed.
2622
pub last_account_key: B256,
27-
/// The last walker key processed.
28-
pub last_walker_key: Nibbles,
2923
}
3024

3125
impl From<MerkleCheckpoint> for IntermediateStateRootState {
@@ -34,7 +28,6 @@ impl From<MerkleCheckpoint> for IntermediateStateRootState {
3428
hash_builder: HashBuilder::from(value.state),
3529
walker_stack: value.walker_stack.into_iter().map(CursorSubNode::from).collect(),
3630
last_account_key: value.last_account_key,
37-
last_walker_key: Nibbles::from_hex(value.last_walker_key),
3831
}
3932
}
4033
}

0 commit comments

Comments
 (0)