mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
refactor(trie): trie node iterators (#5048)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6448,6 +6448,7 @@ name = "reth-trie"
|
|||||||
version = "0.1.0-alpha.10"
|
version = "0.1.0-alpha.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
|
"auto_impl",
|
||||||
"criterion",
|
"criterion",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|||||||
@ -17,6 +17,7 @@ pub struct MerkleCheckpoint {
|
|||||||
pub target_block: BlockNumber,
|
pub target_block: BlockNumber,
|
||||||
/// The last hashed account key processed.
|
/// The last hashed account key processed.
|
||||||
pub last_account_key: B256,
|
pub last_account_key: B256,
|
||||||
|
// TODO: remove in the next breaking release.
|
||||||
/// The last walker key processed.
|
/// The last walker key processed.
|
||||||
pub last_walker_key: Vec<u8>,
|
pub last_walker_key: Vec<u8>,
|
||||||
/// Previously recorded walker stack.
|
/// Previously recorded walker stack.
|
||||||
@ -30,11 +31,16 @@ impl MerkleCheckpoint {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
target_block: BlockNumber,
|
target_block: BlockNumber,
|
||||||
last_account_key: B256,
|
last_account_key: B256,
|
||||||
last_walker_key: Vec<u8>,
|
|
||||||
walker_stack: Vec<StoredSubNode>,
|
walker_stack: Vec<StoredSubNode>,
|
||||||
state: HashBuilderState,
|
state: HashBuilderState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { target_block, last_account_key, last_walker_key, walker_stack, state }
|
Self {
|
||||||
|
target_block,
|
||||||
|
last_account_key,
|
||||||
|
walker_stack,
|
||||||
|
state,
|
||||||
|
last_walker_key: Vec::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -224,7 +224,6 @@ impl<DB: Database> Stage<DB> for MerkleStage {
|
|||||||
let checkpoint = MerkleCheckpoint::new(
|
let checkpoint = MerkleCheckpoint::new(
|
||||||
to_block,
|
to_block,
|
||||||
state.last_account_key,
|
state.last_account_key,
|
||||||
state.last_walker_key.hex_data.to_vec(),
|
|
||||||
state.walker_stack.into_iter().map(StoredSubNode::from).collect(),
|
state.walker_stack.into_iter().map(StoredSubNode::from).collect(),
|
||||||
state.hash_builder.into(),
|
state.hash_builder.into(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ tracing.workspace = true
|
|||||||
# misc
|
# misc
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
derive_more = "0.99"
|
derive_more = "0.99"
|
||||||
|
auto_impl = "1"
|
||||||
|
|
||||||
# test-utils
|
# test-utils
|
||||||
triehash = { version = "0.8", optional = true }
|
triehash = { version = "0.8", optional = true }
|
||||||
|
|||||||
@ -34,6 +34,9 @@ pub mod walker;
|
|||||||
mod errors;
|
mod errors;
|
||||||
pub use errors::*;
|
pub use errors::*;
|
||||||
|
|
||||||
|
// The iterators for traversing existing intermediate hashes and updated trie leaves.
|
||||||
|
pub(crate) mod node_iter;
|
||||||
|
|
||||||
/// Merkle proof generation.
|
/// Merkle proof generation.
|
||||||
pub mod proof;
|
pub mod proof;
|
||||||
|
|
||||||
|
|||||||
208
crates/trie/src/node_iter.rs
Normal file
208
crates/trie/src/node_iter.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use crate::{
|
||||||
|
hashed_cursor::{HashedAccountCursor, HashedStorageCursor},
|
||||||
|
trie_cursor::TrieCursor,
|
||||||
|
walker::TrieWalker,
|
||||||
|
StateRootError, StorageRootError,
|
||||||
|
};
|
||||||
|
use reth_primitives::{trie::Nibbles, Account, StorageEntry, B256, U256};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct TrieBranchNode {
|
||||||
|
pub(crate) key: Nibbles,
|
||||||
|
pub(crate) value: B256,
|
||||||
|
pub(crate) children_are_in_trie: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrieBranchNode {
|
||||||
|
pub(crate) fn new(key: Nibbles, value: B256, children_are_in_trie: bool) -> Self {
|
||||||
|
Self { key, value, children_are_in_trie }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum AccountNode {
|
||||||
|
Branch(TrieBranchNode),
|
||||||
|
Leaf(B256, Account),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum StorageNode {
|
||||||
|
Branch(TrieBranchNode),
|
||||||
|
Leaf(B256, U256),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over existing intermediate branch nodes and updated leaf nodes.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct AccountNodeIter<C, H> {
|
||||||
|
/// Underlying walker over intermediate nodes.
|
||||||
|
pub(crate) walker: TrieWalker<C>,
|
||||||
|
/// The cursor for the hashed account entries.
|
||||||
|
pub(crate) hashed_account_cursor: H,
|
||||||
|
/// The previous account key. If the iteration was previously interrupted, this value can be
|
||||||
|
/// used to resume iterating from the last returned leaf node.
|
||||||
|
previous_account_key: Option<B256>,
|
||||||
|
|
||||||
|
/// Current hashed account entry.
|
||||||
|
current_hashed_entry: Option<(B256, Account)>,
|
||||||
|
/// Flag indicating whether we should check the current walker key.
|
||||||
|
current_walker_key_checked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, H> AccountNodeIter<C, H> {
|
||||||
|
pub(crate) fn new(walker: TrieWalker<C>, hashed_account_cursor: H) -> Self {
|
||||||
|
Self {
|
||||||
|
walker,
|
||||||
|
hashed_account_cursor,
|
||||||
|
previous_account_key: None,
|
||||||
|
current_hashed_entry: None,
|
||||||
|
current_walker_key_checked: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_last_account_key(mut self, previous_account_key: B256) -> Self {
|
||||||
|
self.previous_account_key = Some(previous_account_key);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, H> AccountNodeIter<C, H>
|
||||||
|
where
|
||||||
|
C: TrieCursor,
|
||||||
|
H: HashedAccountCursor,
|
||||||
|
{
|
||||||
|
/// Return the next account trie node to be added to the hash builder.
|
||||||
|
///
|
||||||
|
/// Returns the nodes using this algorithm:
|
||||||
|
/// 1. Return the current intermediate branch node if it hasn't been updated.
|
||||||
|
/// 2. Advance the trie walker to the next intermediate branch node and retrieve next
|
||||||
|
/// unprocessed key.
|
||||||
|
/// 3. Reposition the hashed account cursor on the next unprocessed key.
|
||||||
|
/// 4. Return every hashed account entry up to the key of the current intermediate branch node.
|
||||||
|
/// 5. Repeat.
|
||||||
|
///
|
||||||
|
/// NOTE: The iteration will start from the key of the previous hashed entry if it was supplied.
|
||||||
|
pub(crate) fn try_next(&mut self) -> Result<Option<AccountNode>, StateRootError> {
|
||||||
|
loop {
|
||||||
|
if let Some(key) = self.walker.key() {
|
||||||
|
if !self.current_walker_key_checked && self.previous_account_key.is_none() {
|
||||||
|
self.current_walker_key_checked = true;
|
||||||
|
if self.walker.can_skip_current_node {
|
||||||
|
return Ok(Some(AccountNode::Branch(TrieBranchNode::new(
|
||||||
|
key,
|
||||||
|
self.walker.hash().unwrap(),
|
||||||
|
self.walker.children_are_in_trie(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((hashed_address, account)) = self.current_hashed_entry.take() {
|
||||||
|
if self.walker.key().map_or(false, |key| key < Nibbles::unpack(hashed_address)) {
|
||||||
|
self.current_walker_key_checked = false;
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_hashed_entry = self.hashed_account_cursor.next()?;
|
||||||
|
return Ok(Some(AccountNode::Leaf(hashed_address, account)))
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.previous_account_key.take() {
|
||||||
|
Some(account_key) => {
|
||||||
|
self.hashed_account_cursor.seek(account_key)?;
|
||||||
|
self.current_hashed_entry = self.hashed_account_cursor.next()?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let seek_key = match self.walker.next_unprocessed_key() {
|
||||||
|
Some(key) => key,
|
||||||
|
None => break, // no more keys
|
||||||
|
};
|
||||||
|
self.current_hashed_entry = self.hashed_account_cursor.seek(seek_key)?;
|
||||||
|
self.walker.advance()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct StorageNodeIter<C, H> {
|
||||||
|
/// Underlying walker over intermediate nodes.
|
||||||
|
pub(crate) walker: TrieWalker<C>,
|
||||||
|
/// The cursor for the hashed storage entries.
|
||||||
|
pub(crate) hashed_storage_cursor: H,
|
||||||
|
/// The hashed address this storage trie belongs to.
|
||||||
|
hashed_address: B256,
|
||||||
|
|
||||||
|
/// Current hashed storage entry.
|
||||||
|
current_hashed_entry: Option<StorageEntry>,
|
||||||
|
/// Flag indicating whether we should check the current walker key.
|
||||||
|
current_walker_key_checked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, H> StorageNodeIter<C, H> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
walker: TrieWalker<C>,
|
||||||
|
hashed_storage_cursor: H,
|
||||||
|
hashed_address: B256,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
walker,
|
||||||
|
hashed_storage_cursor,
|
||||||
|
hashed_address,
|
||||||
|
current_walker_key_checked: false,
|
||||||
|
current_hashed_entry: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, H> StorageNodeIter<C, H>
|
||||||
|
where
|
||||||
|
C: TrieCursor,
|
||||||
|
H: HashedStorageCursor,
|
||||||
|
{
|
||||||
|
/// Return the next storage trie node to be added to the hash builder.
|
||||||
|
///
|
||||||
|
/// Returns the nodes using this algorithm:
|
||||||
|
/// 1. Return the current intermediate branch node if it hasn't been updated.
|
||||||
|
/// 2. Advance the trie walker to the next intermediate branch node and retrieve next
|
||||||
|
/// unprocessed key.
|
||||||
|
/// 3. Reposition the hashed storage cursor on the next unprocessed key.
|
||||||
|
/// 4. Return every hashed storage entry up to the key of the current intermediate branch node.
|
||||||
|
/// 5. Repeat.
|
||||||
|
pub(crate) fn try_next(&mut self) -> Result<Option<StorageNode>, StorageRootError> {
|
||||||
|
loop {
|
||||||
|
if let Some(key) = self.walker.key() {
|
||||||
|
if !self.current_walker_key_checked {
|
||||||
|
self.current_walker_key_checked = true;
|
||||||
|
if self.walker.can_skip_current_node {
|
||||||
|
return Ok(Some(StorageNode::Branch(TrieBranchNode::new(
|
||||||
|
key,
|
||||||
|
self.walker.hash().unwrap(),
|
||||||
|
self.walker.children_are_in_trie(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(StorageEntry { key: hashed_key, value }) = self.current_hashed_entry.take()
|
||||||
|
{
|
||||||
|
if self.walker.key().map_or(false, |key| key < Nibbles::unpack(hashed_key)) {
|
||||||
|
self.current_walker_key_checked = false;
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_hashed_entry = self.hashed_storage_cursor.next()?;
|
||||||
|
return Ok(Some(StorageNode::Leaf(hashed_key, value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(seek_key) = self.walker.next_unprocessed_key() else { break };
|
||||||
|
self.current_hashed_entry =
|
||||||
|
self.hashed_storage_cursor.seek(self.hashed_address, seek_key)?;
|
||||||
|
self.walker.advance()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,5 @@
|
|||||||
use crate::{trie_cursor::CursorSubNode, updates::TrieUpdates};
|
use crate::{trie_cursor::CursorSubNode, updates::TrieUpdates};
|
||||||
use reth_primitives::{
|
use reth_primitives::{stage::MerkleCheckpoint, trie::hash_builder::HashBuilder, B256};
|
||||||
stage::MerkleCheckpoint,
|
|
||||||
trie::{hash_builder::HashBuilder, Nibbles},
|
|
||||||
B256,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The progress of the state root computation.
|
/// The progress of the state root computation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -24,8 +20,6 @@ pub struct IntermediateStateRootState {
|
|||||||
pub walker_stack: Vec<CursorSubNode>,
|
pub walker_stack: Vec<CursorSubNode>,
|
||||||
/// The last hashed account key processed.
|
/// The last hashed account key processed.
|
||||||
pub last_account_key: B256,
|
pub last_account_key: B256,
|
||||||
/// The last walker key processed.
|
|
||||||
pub last_walker_key: Nibbles,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MerkleCheckpoint> for IntermediateStateRootState {
|
impl From<MerkleCheckpoint> for IntermediateStateRootState {
|
||||||
@ -34,7 +28,6 @@ impl From<MerkleCheckpoint> for IntermediateStateRootState {
|
|||||||
hash_builder: HashBuilder::from(value.state),
|
hash_builder: HashBuilder::from(value.state),
|
||||||
walker_stack: value.walker_stack.into_iter().map(CursorSubNode::from).collect(),
|
walker_stack: value.walker_stack.into_iter().map(CursorSubNode::from).collect(),
|
||||||
last_account_key: value.last_account_key,
|
last_account_key: value.last_account_key,
|
||||||
last_walker_key: Nibbles::from_hex(value.last_walker_key),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
account::EthAccount,
|
account::EthAccount,
|
||||||
hashed_cursor::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor},
|
hashed_cursor::{HashedCursorFactory, HashedStorageCursor},
|
||||||
|
node_iter::{AccountNode, AccountNodeIter, StorageNode, StorageNodeIter},
|
||||||
prefix_set::PrefixSetMut,
|
prefix_set::PrefixSetMut,
|
||||||
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
|
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
|
||||||
walker::TrieWalker,
|
walker::TrieWalker,
|
||||||
StorageRootError,
|
StateRootError, StorageRootError,
|
||||||
};
|
};
|
||||||
use alloy_rlp::{BufMut, Encodable};
|
use alloy_rlp::{BufMut, Encodable};
|
||||||
use reth_db::{tables, transaction::DbTx};
|
use reth_db::{tables, transaction::DbTx};
|
||||||
@ -12,7 +13,7 @@ use reth_primitives::{
|
|||||||
keccak256,
|
keccak256,
|
||||||
proofs::EMPTY_ROOT,
|
proofs::EMPTY_ROOT,
|
||||||
trie::{AccountProof, HashBuilder, Nibbles, StorageProof},
|
trie::{AccountProof, HashBuilder, Nibbles, StorageProof},
|
||||||
Address, StorageEntry, B256,
|
Address, B256,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A struct for generating merkle proofs.
|
/// A struct for generating merkle proofs.
|
||||||
@ -45,65 +46,46 @@ where
|
|||||||
&self,
|
&self,
|
||||||
address: Address,
|
address: Address,
|
||||||
slots: &[B256],
|
slots: &[B256],
|
||||||
) -> Result<AccountProof, StorageRootError> {
|
) -> Result<AccountProof, StateRootError> {
|
||||||
let target_hashed_address = keccak256(address);
|
let target_hashed_address = keccak256(address);
|
||||||
let target_nibbles = Nibbles::unpack(target_hashed_address);
|
let target_nibbles = Nibbles::unpack(target_hashed_address);
|
||||||
let mut account_proof = AccountProof::new(address);
|
let mut account_proof = AccountProof::new(address);
|
||||||
|
|
||||||
let mut trie_cursor =
|
let hashed_account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
|
||||||
AccountTrieCursor::new(self.tx.cursor_read::<tables::AccountsTrie>()?);
|
let trie_cursor = AccountTrieCursor::new(self.tx.cursor_read::<tables::AccountsTrie>()?);
|
||||||
let mut hashed_account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
|
|
||||||
|
|
||||||
// Create the walker.
|
// Create the walker.
|
||||||
let mut prefix_set = PrefixSetMut::default();
|
let mut prefix_set = PrefixSetMut::default();
|
||||||
prefix_set.insert(target_nibbles.clone());
|
prefix_set.insert(target_nibbles.clone());
|
||||||
let mut walker = TrieWalker::new(&mut trie_cursor, prefix_set.freeze());
|
let walker = TrieWalker::new(trie_cursor, prefix_set.freeze());
|
||||||
|
|
||||||
// Create a hash builder to rebuild the root node since it is not available in the database.
|
// Create a hash builder to rebuild the root node since it is not available in the database.
|
||||||
let mut hash_builder =
|
let mut hash_builder =
|
||||||
HashBuilder::default().with_proof_retainer(Vec::from([target_nibbles.clone()]));
|
HashBuilder::default().with_proof_retainer(Vec::from([target_nibbles.clone()]));
|
||||||
|
|
||||||
let mut account_rlp = Vec::with_capacity(128);
|
let mut account_rlp = Vec::with_capacity(128);
|
||||||
while let Some(key) = walker.key() {
|
let mut account_node_iter = AccountNodeIter::new(walker, hashed_account_cursor);
|
||||||
if walker.can_skip_current_node {
|
while let Some(account_node) = account_node_iter.try_next()? {
|
||||||
let value = walker.hash().unwrap();
|
match account_node {
|
||||||
let is_in_db_trie = walker.children_are_in_trie();
|
AccountNode::Branch(node) => {
|
||||||
hash_builder.add_branch(key.clone(), value, is_in_db_trie);
|
hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
|
||||||
}
|
|
||||||
|
|
||||||
let seek_key = match walker.next_unprocessed_key() {
|
|
||||||
Some(key) => key,
|
|
||||||
None => break, // no more keys
|
|
||||||
};
|
|
||||||
|
|
||||||
let next_key = walker.advance()?;
|
|
||||||
let mut next_account_entry = hashed_account_cursor.seek(seek_key)?;
|
|
||||||
while let Some((hashed_address, account)) = next_account_entry {
|
|
||||||
let account_nibbles = Nibbles::unpack(hashed_address);
|
|
||||||
|
|
||||||
if let Some(ref key) = next_key {
|
|
||||||
if key < &account_nibbles {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
AccountNode::Leaf(hashed_address, account) => {
|
||||||
|
let storage_root = if hashed_address == target_hashed_address {
|
||||||
|
let (storage_root, storage_proofs) =
|
||||||
|
self.storage_root_with_proofs(hashed_address, slots)?;
|
||||||
|
account_proof.set_account(account, storage_root, storage_proofs);
|
||||||
|
storage_root
|
||||||
|
} else {
|
||||||
|
self.storage_root(hashed_address)?
|
||||||
|
};
|
||||||
|
|
||||||
let storage_root = if hashed_address == target_hashed_address {
|
account_rlp.clear();
|
||||||
let (storage_root, storage_proofs) =
|
let account = EthAccount::from(account).with_storage_root(storage_root);
|
||||||
self.storage_root_with_proofs(hashed_address, slots)?;
|
account.encode(&mut account_rlp as &mut dyn BufMut);
|
||||||
account_proof.set_account(account, storage_root, storage_proofs);
|
|
||||||
storage_root
|
|
||||||
} else {
|
|
||||||
self.storage_root(hashed_address)?
|
|
||||||
};
|
|
||||||
|
|
||||||
account_rlp.clear();
|
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
|
||||||
let account = EthAccount::from(account).with_storage_root(storage_root);
|
}
|
||||||
account.encode(&mut &mut account_rlp as &mut dyn BufMut);
|
|
||||||
|
|
||||||
hash_builder.add_leaf(account_nibbles, &account_rlp);
|
|
||||||
|
|
||||||
// Move the next account entry
|
|
||||||
next_account_entry = hashed_account_cursor.next()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,11 +111,6 @@ where
|
|||||||
) -> Result<(B256, Vec<StorageProof>), StorageRootError> {
|
) -> Result<(B256, Vec<StorageProof>), StorageRootError> {
|
||||||
let mut hashed_storage_cursor = self.hashed_cursor_factory.hashed_storage_cursor()?;
|
let mut hashed_storage_cursor = self.hashed_cursor_factory.hashed_storage_cursor()?;
|
||||||
|
|
||||||
let mut trie_cursor = StorageTrieCursor::new(
|
|
||||||
self.tx.cursor_dup_read::<tables::StoragesTrie>()?,
|
|
||||||
hashed_address,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut proofs = slots.iter().copied().map(StorageProof::new).collect::<Vec<_>>();
|
let mut proofs = slots.iter().copied().map(StorageProof::new).collect::<Vec<_>>();
|
||||||
|
|
||||||
// short circuit on empty storage
|
// short circuit on empty storage
|
||||||
@ -143,52 +120,41 @@ where
|
|||||||
|
|
||||||
let target_nibbles = proofs.iter().map(|p| p.nibbles.clone()).collect::<Vec<_>>();
|
let target_nibbles = proofs.iter().map(|p| p.nibbles.clone()).collect::<Vec<_>>();
|
||||||
let prefix_set = PrefixSetMut::from(target_nibbles.clone()).freeze();
|
let prefix_set = PrefixSetMut::from(target_nibbles.clone()).freeze();
|
||||||
let mut walker = TrieWalker::new(&mut trie_cursor, prefix_set);
|
let trie_cursor = StorageTrieCursor::new(
|
||||||
|
self.tx.cursor_dup_read::<tables::StoragesTrie>()?,
|
||||||
|
hashed_address,
|
||||||
|
);
|
||||||
|
let walker = TrieWalker::new(trie_cursor, prefix_set);
|
||||||
|
|
||||||
let mut hash_builder = HashBuilder::default().with_proof_retainer(target_nibbles);
|
let mut hash_builder = HashBuilder::default().with_proof_retainer(target_nibbles);
|
||||||
while let Some(key) = walker.key() {
|
let mut storage_node_iter =
|
||||||
if walker.can_skip_current_node {
|
StorageNodeIter::new(walker, hashed_storage_cursor, hashed_address);
|
||||||
hash_builder.add_branch(key, walker.hash().unwrap(), walker.children_are_in_trie());
|
while let Some(node) = storage_node_iter.try_next()? {
|
||||||
}
|
match node {
|
||||||
|
StorageNode::Branch(node) => {
|
||||||
let seek_key = match walker.next_unprocessed_key() {
|
hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
|
||||||
Some(key) => key,
|
}
|
||||||
None => break, // no more keys
|
StorageNode::Leaf(hashed_slot, value) => {
|
||||||
};
|
let nibbles = Nibbles::unpack(hashed_slot);
|
||||||
|
if let Some(proof) = proofs.iter_mut().find(|proof| proof.nibbles == nibbles) {
|
||||||
let next_key = walker.advance()?;
|
proof.set_value(value);
|
||||||
let mut storage = hashed_storage_cursor.seek(hashed_address, seek_key)?;
|
|
||||||
while let Some(StorageEntry { key: hashed_key, value }) = storage {
|
|
||||||
let hashed_key_nibbles = Nibbles::unpack(hashed_key);
|
|
||||||
if let Some(ref key) = next_key {
|
|
||||||
if key < &hashed_key_nibbles {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
hash_builder.add_leaf(nibbles, alloy_rlp::encode_fixed_size(&value).as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(proof) =
|
|
||||||
proofs.iter_mut().find(|proof| proof.nibbles == hashed_key_nibbles)
|
|
||||||
{
|
|
||||||
proof.set_value(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_builder
|
|
||||||
.add_leaf(hashed_key_nibbles, alloy_rlp::encode_fixed_size(&value).as_ref());
|
|
||||||
storage = hashed_storage_cursor.next()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = hash_builder.root();
|
let root = hash_builder.root();
|
||||||
|
|
||||||
let proof_nodes = hash_builder.take_proofs();
|
let all_proof_nodes = hash_builder.take_proofs();
|
||||||
for proof in proofs.iter_mut() {
|
for proof in proofs.iter_mut() {
|
||||||
proof.set_proof(
|
// Iterate over all proof nodes and find the matching ones.
|
||||||
proof_nodes
|
// The filtered results are guaranteed to be in order.
|
||||||
.iter()
|
let matching_proof_nodes = all_proof_nodes
|
||||||
.filter(|(path, _)| proof.nibbles.starts_with(path))
|
.iter()
|
||||||
.map(|(_, node)| node.clone())
|
.filter(|(path, _)| proof.nibbles.starts_with(path))
|
||||||
.collect(),
|
.map(|(_, node)| node.clone());
|
||||||
);
|
proof.set_proof(matching_proof_nodes.collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((root, proofs))
|
Ok((root, proofs))
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
account::EthAccount,
|
account::EthAccount,
|
||||||
hashed_cursor::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor},
|
hashed_cursor::{HashedCursorFactory, HashedStorageCursor},
|
||||||
|
node_iter::{AccountNode, AccountNodeIter, StorageNode, StorageNodeIter},
|
||||||
prefix_set::{PrefixSet, PrefixSetLoader, PrefixSetMut},
|
prefix_set::{PrefixSet, PrefixSetLoader, PrefixSetMut},
|
||||||
progress::{IntermediateStateRootState, StateRootProgress},
|
progress::{IntermediateStateRootState, StateRootProgress},
|
||||||
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
|
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
|
||||||
@ -8,13 +9,13 @@ use crate::{
|
|||||||
walker::TrieWalker,
|
walker::TrieWalker,
|
||||||
StateRootError, StorageRootError,
|
StateRootError, StorageRootError,
|
||||||
};
|
};
|
||||||
use alloy_rlp::Encodable;
|
use alloy_rlp::{BufMut, Encodable};
|
||||||
use reth_db::{tables, transaction::DbTx};
|
use reth_db::{tables, transaction::DbTx};
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
keccak256,
|
keccak256,
|
||||||
proofs::EMPTY_ROOT,
|
proofs::EMPTY_ROOT,
|
||||||
trie::{HashBuilder, Nibbles},
|
trie::{HashBuilder, Nibbles},
|
||||||
Address, BlockNumber, StorageEntry, B256,
|
Address, BlockNumber, B256,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
@ -224,136 +225,104 @@ where
|
|||||||
tracing::debug!(target: "loader", "calculating state root");
|
tracing::debug!(target: "loader", "calculating state root");
|
||||||
let mut trie_updates = TrieUpdates::default();
|
let mut trie_updates = TrieUpdates::default();
|
||||||
|
|
||||||
let mut hashed_account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
|
let hashed_account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
|
||||||
let mut trie_cursor =
|
let trie_cursor = AccountTrieCursor::new(self.tx.cursor_read::<tables::AccountsTrie>()?);
|
||||||
AccountTrieCursor::new(self.tx.cursor_read::<tables::AccountsTrie>()?);
|
|
||||||
|
|
||||||
let (mut walker, mut hash_builder, mut last_account_key, mut last_walker_key) =
|
let (mut hash_builder, mut account_node_iter) = match self.previous_state {
|
||||||
match self.previous_state {
|
Some(state) => {
|
||||||
Some(state) => (
|
let walker = TrieWalker::from_stack(
|
||||||
TrieWalker::from_stack(
|
trie_cursor,
|
||||||
&mut trie_cursor,
|
state.walker_stack,
|
||||||
state.walker_stack,
|
self.changed_account_prefixes,
|
||||||
self.changed_account_prefixes,
|
);
|
||||||
),
|
(
|
||||||
state.hash_builder,
|
state.hash_builder,
|
||||||
Some(state.last_account_key),
|
AccountNodeIter::new(walker, hashed_account_cursor)
|
||||||
Some(state.last_walker_key),
|
.with_last_account_key(state.last_account_key),
|
||||||
),
|
)
|
||||||
None => (
|
}
|
||||||
TrieWalker::new(&mut trie_cursor, self.changed_account_prefixes),
|
None => {
|
||||||
HashBuilder::default(),
|
let walker = TrieWalker::new(trie_cursor, self.changed_account_prefixes);
|
||||||
None,
|
(HashBuilder::default(), AccountNodeIter::new(walker, hashed_account_cursor))
|
||||||
None,
|
}
|
||||||
),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
walker.set_updates(retain_updates);
|
account_node_iter.walker.set_updates(retain_updates);
|
||||||
hash_builder.set_updates(retain_updates);
|
hash_builder.set_updates(retain_updates);
|
||||||
|
|
||||||
let mut account_rlp = Vec::with_capacity(128);
|
let mut account_rlp = Vec::with_capacity(128);
|
||||||
let mut hashed_entries_walked = 0;
|
let mut hashed_entries_walked = 0;
|
||||||
|
while let Some(node) = account_node_iter.try_next()? {
|
||||||
while let Some(key) = last_walker_key.take().or_else(|| walker.key()) {
|
match node {
|
||||||
// Take the last account key to make sure we take it into consideration only once.
|
AccountNode::Branch(node) => {
|
||||||
let (next_key, mut next_account_entry) = match last_account_key.take() {
|
hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
|
||||||
// Seek the last processed entry and take the next after.
|
|
||||||
Some(account_key) => {
|
|
||||||
hashed_account_cursor.seek(account_key)?;
|
|
||||||
(walker.key(), hashed_account_cursor.next()?)
|
|
||||||
}
|
}
|
||||||
None => {
|
AccountNode::Leaf(hashed_address, account) => {
|
||||||
if walker.can_skip_current_node {
|
hashed_entries_walked += 1;
|
||||||
let value = walker.hash().unwrap();
|
|
||||||
let is_in_db_trie = walker.children_are_in_trie();
|
|
||||||
hash_builder.add_branch(key.clone(), value, is_in_db_trie);
|
|
||||||
}
|
|
||||||
|
|
||||||
let seek_key = match walker.next_unprocessed_key() {
|
// We assume we can always calculate a storage root without
|
||||||
Some(key) => key,
|
// OOMing. This opens us up to a potential DOS vector if
|
||||||
None => break, // no more keys
|
// a contract had too many storage entries and they were
|
||||||
|
// all buffered w/o us returning and committing our intermediate
|
||||||
|
// progress.
|
||||||
|
// TODO: We can consider introducing the TrieProgress::Progress/Complete
|
||||||
|
// abstraction inside StorageRoot, but let's give it a try as-is for now.
|
||||||
|
let storage_root_calculator = StorageRoot::new_hashed(self.tx, hashed_address)
|
||||||
|
.with_hashed_cursor_factory(self.hashed_cursor_factory.clone())
|
||||||
|
.with_changed_prefixes(
|
||||||
|
self.changed_storage_prefixes
|
||||||
|
.get(&hashed_address)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let storage_root = if retain_updates {
|
||||||
|
let (root, storage_slots_walked, updates) =
|
||||||
|
storage_root_calculator.root_with_updates()?;
|
||||||
|
hashed_entries_walked += storage_slots_walked;
|
||||||
|
trie_updates.extend(updates.into_iter());
|
||||||
|
root
|
||||||
|
} else {
|
||||||
|
storage_root_calculator.root()?
|
||||||
};
|
};
|
||||||
|
|
||||||
(walker.advance()?, hashed_account_cursor.seek(seek_key)?)
|
let account = EthAccount::from(account).with_storage_root(storage_root);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Some((hashed_address, account)) = next_account_entry {
|
account_rlp.clear();
|
||||||
hashed_entries_walked += 1;
|
account.encode(&mut account_rlp as &mut dyn BufMut);
|
||||||
let account_nibbles = Nibbles::unpack(hashed_address);
|
|
||||||
|
|
||||||
if let Some(ref key) = next_key {
|
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
|
||||||
if key < &account_nibbles {
|
|
||||||
tracing::trace!(target: "loader", "breaking, already detected");
|
// Decide if we need to return intermediate progress.
|
||||||
break
|
let total_updates_len = trie_updates.len() +
|
||||||
|
account_node_iter.walker.updates_len() +
|
||||||
|
hash_builder.updates_len();
|
||||||
|
if retain_updates && total_updates_len as u64 >= self.threshold {
|
||||||
|
let (walker_stack, walker_updates) = account_node_iter.walker.split();
|
||||||
|
let (hash_builder, hash_builder_updates) = hash_builder.split();
|
||||||
|
|
||||||
|
let state = IntermediateStateRootState {
|
||||||
|
hash_builder,
|
||||||
|
walker_stack,
|
||||||
|
last_account_key: hashed_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
trie_updates.extend(walker_updates.into_iter());
|
||||||
|
trie_updates.extend_with_account_updates(hash_builder_updates);
|
||||||
|
|
||||||
|
return Ok(StateRootProgress::Progress(
|
||||||
|
Box::new(state),
|
||||||
|
hashed_entries_walked,
|
||||||
|
trie_updates,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume we can always calculate a storage root without
|
|
||||||
// OOMing. This opens us up to a potential DOS vector if
|
|
||||||
// a contract had too many storage entries and they were
|
|
||||||
// all buffered w/o us returning and committing our intermediate
|
|
||||||
// progress.
|
|
||||||
// TODO: We can consider introducing the TrieProgress::Progress/Complete
|
|
||||||
// abstraction inside StorageRoot, but let's give it a try as-is for now.
|
|
||||||
let storage_root_calculator = StorageRoot::new_hashed(self.tx, hashed_address)
|
|
||||||
.with_hashed_cursor_factory(self.hashed_cursor_factory.clone())
|
|
||||||
.with_changed_prefixes(
|
|
||||||
self.changed_storage_prefixes
|
|
||||||
.get(&hashed_address)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let storage_root = if retain_updates {
|
|
||||||
let (root, storage_slots_walked, updates) =
|
|
||||||
storage_root_calculator.root_with_updates()?;
|
|
||||||
hashed_entries_walked += storage_slots_walked;
|
|
||||||
trie_updates.extend(updates.into_iter());
|
|
||||||
root
|
|
||||||
} else {
|
|
||||||
storage_root_calculator.root()?
|
|
||||||
};
|
|
||||||
|
|
||||||
let account = EthAccount::from(account).with_storage_root(storage_root);
|
|
||||||
|
|
||||||
account_rlp.clear();
|
|
||||||
account.encode(&mut &mut account_rlp);
|
|
||||||
|
|
||||||
hash_builder.add_leaf(account_nibbles, &account_rlp);
|
|
||||||
|
|
||||||
// Decide if we need to return intermediate progress.
|
|
||||||
let total_updates_len =
|
|
||||||
trie_updates.len() + walker.updates_len() + hash_builder.updates_len();
|
|
||||||
if retain_updates && total_updates_len as u64 >= self.threshold {
|
|
||||||
let (walker_stack, walker_updates) = walker.split();
|
|
||||||
let (hash_builder, hash_builder_updates) = hash_builder.split();
|
|
||||||
|
|
||||||
let state = IntermediateStateRootState {
|
|
||||||
hash_builder,
|
|
||||||
walker_stack,
|
|
||||||
last_walker_key: key,
|
|
||||||
last_account_key: hashed_address,
|
|
||||||
};
|
|
||||||
|
|
||||||
trie_updates.extend(walker_updates.into_iter());
|
|
||||||
trie_updates.extend_with_account_updates(hash_builder_updates);
|
|
||||||
|
|
||||||
return Ok(StateRootProgress::Progress(
|
|
||||||
Box::new(state),
|
|
||||||
hashed_entries_walked,
|
|
||||||
trie_updates,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the next account entry
|
|
||||||
next_account_entry = hashed_account_cursor.next()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = hash_builder.root();
|
let root = hash_builder.root();
|
||||||
|
|
||||||
let (_, walker_updates) = walker.split();
|
let (_, walker_updates) = account_node_iter.walker.split();
|
||||||
let (_, hash_builder_updates) = hash_builder.split();
|
let (_, hash_builder_updates) = hash_builder.split();
|
||||||
|
|
||||||
trie_updates.extend(walker_updates.into_iter());
|
trie_updates.extend(walker_updates.into_iter());
|
||||||
@ -464,14 +433,8 @@ where
|
|||||||
retain_updates: bool,
|
retain_updates: bool,
|
||||||
) -> Result<(B256, usize, TrieUpdates), StorageRootError> {
|
) -> Result<(B256, usize, TrieUpdates), StorageRootError> {
|
||||||
tracing::debug!(target: "trie::storage_root", hashed_address = ?self.hashed_address, "calculating storage root");
|
tracing::debug!(target: "trie::storage_root", hashed_address = ?self.hashed_address, "calculating storage root");
|
||||||
|
|
||||||
let mut hashed_storage_cursor = self.hashed_cursor_factory.hashed_storage_cursor()?;
|
let mut hashed_storage_cursor = self.hashed_cursor_factory.hashed_storage_cursor()?;
|
||||||
|
|
||||||
let mut trie_cursor = StorageTrieCursor::new(
|
|
||||||
self.tx.cursor_dup_read::<tables::StoragesTrie>()?,
|
|
||||||
self.hashed_address,
|
|
||||||
);
|
|
||||||
|
|
||||||
// short circuit on empty storage
|
// short circuit on empty storage
|
||||||
if hashed_storage_cursor.is_storage_empty(self.hashed_address)? {
|
if hashed_storage_cursor.is_storage_empty(self.hashed_address)? {
|
||||||
return Ok((
|
return Ok((
|
||||||
@ -481,43 +444,37 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut walker = TrieWalker::new(&mut trie_cursor, self.changed_prefixes.clone())
|
let trie_cursor = StorageTrieCursor::new(
|
||||||
|
self.tx.cursor_dup_read::<tables::StoragesTrie>()?,
|
||||||
|
self.hashed_address,
|
||||||
|
);
|
||||||
|
let walker = TrieWalker::new(trie_cursor, self.changed_prefixes.clone())
|
||||||
.with_updates(retain_updates);
|
.with_updates(retain_updates);
|
||||||
|
|
||||||
let mut hash_builder = HashBuilder::default().with_updates(retain_updates);
|
let mut hash_builder = HashBuilder::default().with_updates(retain_updates);
|
||||||
|
|
||||||
let mut storage_slots_walked = 0;
|
let mut storage_slots_walked = 0;
|
||||||
while let Some(key) = walker.key() {
|
let mut storage_node_iter =
|
||||||
if walker.can_skip_current_node {
|
StorageNodeIter::new(walker, hashed_storage_cursor, self.hashed_address);
|
||||||
hash_builder.add_branch(key, walker.hash().unwrap(), walker.children_are_in_trie());
|
while let Some(node) = storage_node_iter.try_next()? {
|
||||||
}
|
match node {
|
||||||
|
StorageNode::Branch(node) => {
|
||||||
let seek_key = match walker.next_unprocessed_key() {
|
hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
|
||||||
Some(key) => key,
|
}
|
||||||
None => break, // no more keys
|
StorageNode::Leaf(hashed_slot, value) => {
|
||||||
};
|
storage_slots_walked += 1;
|
||||||
|
hash_builder.add_leaf(
|
||||||
let next_key = walker.advance()?;
|
Nibbles::unpack(hashed_slot),
|
||||||
let mut storage = hashed_storage_cursor.seek(self.hashed_address, seek_key)?;
|
alloy_rlp::encode_fixed_size(&value).as_ref(),
|
||||||
while let Some(StorageEntry { key: hashed_key, value }) = storage {
|
);
|
||||||
storage_slots_walked += 1;
|
|
||||||
|
|
||||||
let storage_key_nibbles = Nibbles::unpack(hashed_key);
|
|
||||||
if let Some(ref key) = next_key {
|
|
||||||
if key < &storage_key_nibbles {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hash_builder
|
|
||||||
.add_leaf(storage_key_nibbles, alloy_rlp::encode_fixed_size(&value).as_ref());
|
|
||||||
storage = hashed_storage_cursor.next()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = hash_builder.root();
|
let root = hash_builder.root();
|
||||||
|
|
||||||
let (_, hash_builder_updates) = hash_builder.split();
|
let (_, hash_builder_updates) = hash_builder.split();
|
||||||
let (_, walker_updates) = walker.split();
|
let (_, walker_updates) = storage_node_iter.walker.split();
|
||||||
|
|
||||||
let mut trie_updates = TrieUpdates::default();
|
let mut trie_updates = TrieUpdates::default();
|
||||||
trie_updates.extend(walker_updates.into_iter());
|
trie_updates.extend(walker_updates.into_iter());
|
||||||
@ -529,7 +486,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::mutable_key_type)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::{
|
use crate::test_utils::{
|
||||||
@ -548,7 +504,7 @@ mod tests {
|
|||||||
keccak256,
|
keccak256,
|
||||||
proofs::triehash::KeccakHasher,
|
proofs::triehash::KeccakHasher,
|
||||||
trie::{BranchNodeCompact, TrieMask},
|
trie::{BranchNodeCompact, TrieMask},
|
||||||
Account, Address, B256, MAINNET, U256,
|
Account, Address, StorageEntry, B256, MAINNET, U256,
|
||||||
};
|
};
|
||||||
use reth_provider::{DatabaseProviderRW, ProviderFactory};
|
use reth_provider::{DatabaseProviderRW, ProviderFactory};
|
||||||
use std::{collections::BTreeMap, ops::Mul, str::FromStr};
|
use std::{collections::BTreeMap, ops::Mul, str::FromStr};
|
||||||
@ -621,7 +577,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// TODO: Try to find the edge case by creating some more very complex trie.
|
|
||||||
fn branch_node_child_changes() {
|
fn branch_node_child_changes() {
|
||||||
incremental_vs_full_root(
|
incremental_vs_full_root(
|
||||||
&[
|
&[
|
||||||
|
|||||||
@ -14,20 +14,22 @@ impl<C> AccountTrieCursor<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> TrieCursor<StoredNibbles> for AccountTrieCursor<C>
|
impl<C> TrieCursor for AccountTrieCursor<C>
|
||||||
where
|
where
|
||||||
C: DbCursorRO<tables::AccountsTrie>,
|
C: DbCursorRO<tables::AccountsTrie>,
|
||||||
{
|
{
|
||||||
|
type Key = StoredNibbles;
|
||||||
|
|
||||||
fn seek_exact(
|
fn seek_exact(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: StoredNibbles,
|
key: Self::Key,
|
||||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||||
Ok(self.0.seek_exact(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
Ok(self.0.seek_exact(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek(
|
fn seek(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: StoredNibbles,
|
key: Self::Key,
|
||||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||||
Ok(self.0.seek(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
Ok(self.0.seek(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::updates::TrieKey;
|
use crate::updates::TrieKey;
|
||||||
use reth_db::{table::Key, DatabaseError};
|
use reth_db::DatabaseError;
|
||||||
use reth_primitives::trie::BranchNodeCompact;
|
use reth_primitives::trie::BranchNodeCompact;
|
||||||
|
|
||||||
mod account_cursor;
|
mod account_cursor;
|
||||||
@ -11,13 +11,22 @@ pub use self::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// A cursor for navigating a trie that works with both Tables and DupSort tables.
|
/// A cursor for navigating a trie that works with both Tables and DupSort tables.
|
||||||
pub trait TrieCursor<K: Key> {
|
#[auto_impl::auto_impl(&mut)]
|
||||||
|
pub trait TrieCursor {
|
||||||
|
/// The key type of the cursor.
|
||||||
|
type Key: From<Vec<u8>>;
|
||||||
|
|
||||||
/// Move the cursor to the key and return if it is an exact match.
|
/// Move the cursor to the key and return if it is an exact match.
|
||||||
fn seek_exact(&mut self, key: K)
|
fn seek_exact(
|
||||||
-> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError>;
|
&mut self,
|
||||||
|
key: Self::Key,
|
||||||
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError>;
|
||||||
|
|
||||||
/// Move the cursor to the key and return a value matching of greater than the key.
|
/// Move the cursor to the key and return a value matching of greater than the key.
|
||||||
fn seek(&mut self, key: K) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError>;
|
fn seek(
|
||||||
|
&mut self,
|
||||||
|
key: Self::Key,
|
||||||
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError>;
|
||||||
|
|
||||||
/// Get the current entry.
|
/// Get the current entry.
|
||||||
fn current(&mut self) -> Result<Option<TrieKey>, DatabaseError>;
|
fn current(&mut self) -> Result<Option<TrieKey>, DatabaseError>;
|
||||||
|
|||||||
@ -24,13 +24,15 @@ impl<C> StorageTrieCursor<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> TrieCursor<StoredNibblesSubKey> for StorageTrieCursor<C>
|
impl<C> TrieCursor for StorageTrieCursor<C>
|
||||||
where
|
where
|
||||||
C: DbDupCursorRO<tables::StoragesTrie> + DbCursorRO<tables::StoragesTrie>,
|
C: DbDupCursorRO<tables::StoragesTrie> + DbCursorRO<tables::StoragesTrie>,
|
||||||
{
|
{
|
||||||
|
type Key = StoredNibblesSubKey;
|
||||||
|
|
||||||
fn seek_exact(
|
fn seek_exact(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: StoredNibblesSubKey,
|
key: Self::Key,
|
||||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.cursor
|
.cursor
|
||||||
@ -41,7 +43,7 @@ where
|
|||||||
|
|
||||||
fn seek(
|
fn seek(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: StoredNibblesSubKey,
|
key: Self::Key,
|
||||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.cursor
|
.cursor
|
||||||
|
|||||||
@ -77,7 +77,6 @@ impl TrieUpdates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extend the updates with account trie updates.
|
/// Extend the updates with account trie updates.
|
||||||
#[allow(clippy::mutable_key_type)]
|
|
||||||
pub fn extend_with_account_updates(&mut self, updates: HashMap<Nibbles, BranchNodeCompact>) {
|
pub fn extend_with_account_updates(&mut self, updates: HashMap<Nibbles, BranchNodeCompact>) {
|
||||||
self.extend(updates.into_iter().map(|(nibbles, node)| {
|
self.extend(updates.into_iter().map(|(nibbles, node)| {
|
||||||
(TrieKey::AccountNode(nibbles.hex_data.to_vec().into()), TrieOp::Update(node))
|
(TrieKey::AccountNode(nibbles.hex_data.to_vec().into()), TrieOp::Update(node))
|
||||||
@ -85,7 +84,6 @@ impl TrieUpdates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extend the updates with storage trie updates.
|
/// Extend the updates with storage trie updates.
|
||||||
#[allow(clippy::mutable_key_type)]
|
|
||||||
pub fn extend_with_storage_updates(
|
pub fn extend_with_storage_updates(
|
||||||
&mut self,
|
&mut self,
|
||||||
hashed_address: B256,
|
hashed_address: B256,
|
||||||
|
|||||||
@ -3,19 +3,19 @@ use crate::{
|
|||||||
trie_cursor::{CursorSubNode, TrieCursor},
|
trie_cursor::{CursorSubNode, TrieCursor},
|
||||||
updates::TrieUpdates,
|
updates::TrieUpdates,
|
||||||
};
|
};
|
||||||
use reth_db::{table::Key, DatabaseError};
|
use reth_db::DatabaseError;
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
trie::{BranchNodeCompact, Nibbles},
|
trie::{BranchNodeCompact, Nibbles},
|
||||||
B256,
|
B256,
|
||||||
};
|
};
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// `TrieWalker` is a structure that enables traversal of a Merkle trie.
|
/// `TrieWalker` is a structure that enables traversal of a Merkle trie.
|
||||||
/// It allows moving through the trie in a depth-first manner, skipping certain branches if the .
|
/// It allows moving through the trie in a depth-first manner, skipping certain branches
|
||||||
|
/// if they have not changed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TrieWalker<'a, K, C> {
|
pub struct TrieWalker<C> {
|
||||||
/// A mutable reference to a trie cursor instance used for navigating the trie.
|
/// A mutable reference to a trie cursor instance used for navigating the trie.
|
||||||
pub cursor: &'a mut C,
|
pub cursor: C,
|
||||||
/// A vector containing the trie nodes that have been visited.
|
/// A vector containing the trie nodes that have been visited.
|
||||||
pub stack: Vec<CursorSubNode>,
|
pub stack: Vec<CursorSubNode>,
|
||||||
/// A flag indicating whether the current node can be skipped when traversing the trie. This
|
/// A flag indicating whether the current node can be skipped when traversing the trie. This
|
||||||
@ -26,12 +26,11 @@ pub struct TrieWalker<'a, K, C> {
|
|||||||
pub changes: PrefixSet,
|
pub changes: PrefixSet,
|
||||||
/// The trie updates to be applied to the trie.
|
/// The trie updates to be applied to the trie.
|
||||||
trie_updates: Option<TrieUpdates>,
|
trie_updates: Option<TrieUpdates>,
|
||||||
__phantom: PhantomData<K>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
|
impl<C: TrieCursor> TrieWalker<C> {
|
||||||
/// Constructs a new TrieWalker, setting up the initial state of the stack and cursor.
|
/// Constructs a new TrieWalker, setting up the initial state of the stack and cursor.
|
||||||
pub fn new(cursor: &'a mut C, changes: PrefixSet) -> Self {
|
pub fn new(cursor: C, changes: PrefixSet) -> Self {
|
||||||
// Initialize the walker with a single empty stack element.
|
// Initialize the walker with a single empty stack element.
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
cursor,
|
cursor,
|
||||||
@ -39,7 +38,6 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
|
|||||||
stack: vec![CursorSubNode::default()],
|
stack: vec![CursorSubNode::default()],
|
||||||
can_skip_current_node: false,
|
can_skip_current_node: false,
|
||||||
trie_updates: None,
|
trie_updates: None,
|
||||||
__phantom: PhantomData,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the root node of the trie in the stack, if it exists.
|
// Set up the root node of the trie in the stack, if it exists.
|
||||||
@ -53,15 +51,9 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new TrieWalker from existing stack and a cursor.
|
/// Constructs a new TrieWalker from existing stack and a cursor.
|
||||||
pub fn from_stack(cursor: &'a mut C, stack: Vec<CursorSubNode>, changes: PrefixSet) -> Self {
|
pub fn from_stack(cursor: C, stack: Vec<CursorSubNode>, changes: PrefixSet) -> Self {
|
||||||
let mut this = Self {
|
let mut this =
|
||||||
cursor,
|
Self { cursor, changes, stack, can_skip_current_node: false, trie_updates: None };
|
||||||
changes,
|
|
||||||
stack,
|
|
||||||
can_skip_current_node: false,
|
|
||||||
trie_updates: None,
|
|
||||||
__phantom: PhantomData,
|
|
||||||
};
|
|
||||||
this.update_skip_node();
|
this.update_skip_node();
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -255,7 +247,6 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
prefix_set::PrefixSetMut,
|
prefix_set::PrefixSetMut,
|
||||||
@ -316,10 +307,9 @@ mod tests {
|
|||||||
test_cursor(storage_trie, &expected);
|
test_cursor(storage_trie, &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_cursor<K, T>(mut trie: T, expected: &[Vec<u8>])
|
fn test_cursor<T>(mut trie: T, expected: &[Vec<u8>])
|
||||||
where
|
where
|
||||||
K: Key + From<Vec<u8>>,
|
T: TrieCursor,
|
||||||
T: TrieCursor<K>,
|
|
||||||
{
|
{
|
||||||
let mut walker = TrieWalker::new(&mut trie, Default::default());
|
let mut walker = TrieWalker::new(&mut trie, Default::default());
|
||||||
assert!(walker.key().unwrap().is_empty());
|
assert!(walker.key().unwrap().is_empty());
|
||||||
|
|||||||
Reference in New Issue
Block a user