feat(trie): read-only root calculation (#2233)

This commit is contained in:
Roman Krasiuk
2023-04-21 12:36:48 +03:00
committed by GitHub
parent 9452b3658b
commit ec418d924d
25 changed files with 1279 additions and 494 deletions

View File

@ -1,8 +1,6 @@
use super::TrieCursor;
use reth_db::{
cursor::{DbCursorRO, DbCursorRW},
tables, Error,
};
use crate::updates::TrieKey;
use reth_db::{cursor::DbCursorRO, tables, Error};
use reth_primitives::trie::{BranchNodeCompact, StoredNibbles};
/// A cursor over the account trie.
@ -17,7 +15,7 @@ impl<C> AccountTrieCursor<C> {
impl<'a, C> TrieCursor<StoredNibbles> for AccountTrieCursor<C>
where
C: DbCursorRO<'a, tables::AccountsTrie> + DbCursorRW<'a, tables::AccountsTrie>,
C: DbCursorRO<'a, tables::AccountsTrie>,
{
fn seek_exact(
&mut self,
@ -30,12 +28,8 @@ where
Ok(self.0.seek(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
}
fn upsert(&mut self, key: StoredNibbles, value: BranchNodeCompact) -> Result<(), Error> {
self.0.upsert(key, value)
}
fn delete_current(&mut self) -> Result<(), Error> {
self.0.delete_current()
fn current(&mut self) -> Result<Option<TrieKey>, Error> {
Ok(self.0.current()?.map(|(k, _)| TrieKey::AccountNode(k)))
}
}

View File

@ -1,10 +1,11 @@
use super::TrieCursor;
use crate::updates::TrieKey;
use reth_db::{
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
cursor::{DbCursorRO, DbDupCursorRO},
tables, Error,
};
use reth_primitives::{
trie::{BranchNodeCompact, StorageTrieEntry, StoredNibblesSubKey},
trie::{BranchNodeCompact, StoredNibblesSubKey},
H256,
};
@ -24,10 +25,7 @@ impl<C> StorageTrieCursor<C> {
impl<'a, C> TrieCursor<StoredNibblesSubKey> for StorageTrieCursor<C>
where
C: DbDupCursorRO<'a, tables::StoragesTrie>
+ DbDupCursorRW<'a, tables::StoragesTrie>
+ DbCursorRO<'a, tables::StoragesTrie>
+ DbCursorRW<'a, tables::StoragesTrie>,
C: DbDupCursorRO<'a, tables::StoragesTrie> + DbCursorRO<'a, tables::StoragesTrie>,
{
fn seek_exact(
&mut self,
@ -50,28 +48,18 @@ where
.map(|value| (value.nibbles.inner.to_vec(), value.node)))
}
fn upsert(&mut self, key: StoredNibblesSubKey, value: BranchNodeCompact) -> Result<(), Error> {
if let Some(entry) = self.cursor.seek_by_key_subkey(self.hashed_address, key.clone())? {
// "seek exact"
if entry.nibbles == key {
self.cursor.delete_current()?;
}
}
self.cursor.upsert(self.hashed_address, StorageTrieEntry { nibbles: key, node: value })?;
Ok(())
}
fn delete_current(&mut self) -> Result<(), Error> {
self.cursor.delete_current()
fn current(&mut self) -> Result<Option<TrieKey>, Error> {
Ok(self.cursor.current()?.map(|(k, v)| TrieKey::StorageNode(k, v.nibbles)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use reth_db::{mdbx::test_utils::create_test_rw_db, tables, transaction::DbTxMut};
use reth_primitives::trie::BranchNodeCompact;
use reth_db::{
cursor::DbCursorRW, mdbx::test_utils::create_test_rw_db, tables, transaction::DbTxMut,
};
use reth_primitives::trie::{BranchNodeCompact, StorageTrieEntry};
use reth_provider::Transaction;
// tests that upsert and seek match on the storagetrie cursor
@ -79,14 +67,20 @@ mod tests {
fn test_storage_cursor_abstraction() {
let db = create_test_rw_db();
let tx = Transaction::new(db.as_ref()).unwrap();
let cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
let mut cursor = StorageTrieCursor::new(cursor, H256::random());
let mut cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
let hashed_address = H256::random();
let key = vec![0x2, 0x3];
let value = BranchNodeCompact::new(1, 1, 1, vec![H256::random()], None);
cursor.upsert(key.clone().into(), value.clone()).unwrap();
assert_eq!(cursor.seek(key.into()).unwrap().unwrap().1, value);
cursor
.upsert(
hashed_address,
StorageTrieEntry { nibbles: key.clone().into(), node: value.clone() },
)
.unwrap();
let mut cursor = StorageTrieCursor::new(cursor, hashed_address);
assert_eq!(cursor.seek(key.clone().into()).unwrap().unwrap().1, value);
}
}

View File

@ -1,5 +1,8 @@
use crate::{nodes::CHILD_INDEX_RANGE, Nibbles};
use reth_primitives::{trie::BranchNodeCompact, H256};
use reth_primitives::{
trie::{BranchNodeCompact, StoredSubNode},
H256,
};
/// Cursor for iterating over a subtrie.
#[derive(Clone)]
@ -31,6 +34,23 @@ impl std::fmt::Debug for CursorSubNode {
}
}
impl From<StoredSubNode> for CursorSubNode {
fn from(value: StoredSubNode) -> Self {
let nibble = match value.nibble {
Some(n) => n as i8,
None => -1,
};
Self { key: Nibbles::from(value.key), nibble, node: value.node }
}
}
impl From<CursorSubNode> for StoredSubNode {
fn from(value: CursorSubNode) -> Self {
let nibble = if value.nibble >= 0 { Some(value.nibble as u8) } else { None };
Self { key: value.key.hex_data, nibble, node: value.node }
}
}
impl CursorSubNode {
/// Creates a new `CursorSubNode` from a key and an optional node.
pub fn new(key: Nibbles, node: Option<BranchNodeCompact>) -> Self {

View File

@ -1,3 +1,4 @@
use crate::updates::TrieKey;
use reth_db::{table::Key, Error};
use reth_primitives::trie::BranchNodeCompact;
@ -9,9 +10,6 @@ pub trait TrieCursor<K: 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)>, Error>;
/// Upsert the key/value pair.
fn upsert(&mut self, key: K, value: BranchNodeCompact) -> Result<(), Error>;
/// Delete the key/value pair at the current cursor position.
fn delete_current(&mut self) -> Result<(), Error>;
/// Get the current entry.
fn current(&mut self) -> Result<Option<TrieKey>, Error>;
}

View File

@ -5,13 +5,10 @@ use crate::{
use reth_primitives::{
keccak256,
proofs::EMPTY_ROOT,
trie::{BranchNodeCompact, TrieMask},
trie::{BranchNodeCompact, HashBuilderState, HashBuilderValue, TrieMask},
H256,
};
use std::{fmt::Debug, sync::mpsc};
mod value;
use value::HashBuilderValue;
use std::{collections::BTreeMap, fmt::Debug, sync::mpsc};
/// A type alias for a sender of branch nodes.
/// Branch nodes are sent by the Hash Builder to be stored in the database.
@ -40,7 +37,7 @@ pub type BranchNodeSender = mpsc::Sender<(Nibbles, BranchNodeCompact)>;
/// up, combining the hashes of child nodes and ultimately generating the root hash. The root hash
/// can then be used to verify the integrity and authenticity of the trie's data by constructing and
/// verifying Merkle proofs.
#[derive(Clone, Debug, Default)]
#[derive(Debug, Default)]
pub struct HashBuilder {
key: Nibbles,
stack: Vec<Vec<u8>>,
@ -52,19 +49,66 @@ pub struct HashBuilder {
stored_in_database: bool,
branch_node_sender: Option<BranchNodeSender>,
updated_branch_nodes: Option<BTreeMap<Nibbles, BranchNodeCompact>>,
}
impl From<HashBuilderState> for HashBuilder {
fn from(state: HashBuilderState) -> Self {
Self {
key: Nibbles::from(state.key),
stack: state.stack,
value: state.value,
groups: state.groups,
tree_masks: state.tree_masks,
hash_masks: state.hash_masks,
stored_in_database: state.stored_in_database,
updated_branch_nodes: None,
}
}
}
impl From<HashBuilder> for HashBuilderState {
fn from(state: HashBuilder) -> Self {
Self {
key: state.key.hex_data,
stack: state.stack,
value: state.value,
groups: state.groups,
tree_masks: state.tree_masks,
hash_masks: state.hash_masks,
stored_in_database: state.stored_in_database,
}
}
}
impl HashBuilder {
/// Creates a new instance of the Hash Builder.
pub fn new(store_tx: Option<BranchNodeSender>) -> Self {
Self { branch_node_sender: store_tx, ..Default::default() }
/// Enables the Hash Builder to store updated branch nodes.
///
/// Call [HashBuilder::split] to get the updates to branch nodes.
pub fn with_updates(mut self, retain_updates: bool) -> Self {
self.set_updates(retain_updates);
self
}
/// Set a branch node sender on the Hash Builder instance.
pub fn with_branch_node_sender(mut self, tx: BranchNodeSender) -> Self {
self.branch_node_sender = Some(tx);
self
/// Enables the Hash Builder to store updated branch nodes.
///
/// Call [HashBuilder::split] to get the updates to branch nodes.
pub fn set_updates(&mut self, retain_updates: bool) {
if retain_updates {
self.updated_branch_nodes = Some(BTreeMap::default());
}
}
/// Splits the [HashBuilder] into a [HashBuilder] and hash builder updates.
pub fn split(mut self) -> (Self, BTreeMap<Nibbles, BranchNodeCompact>) {
let updates = self.updated_branch_nodes.take();
(self, updates.unwrap_or_default())
}
/// The number of total updates accrued.
/// Returns `0` if [Self::with_updates] was not called.
pub fn updates_len(&self) -> usize {
self.updated_branch_nodes.as_ref().map(|u| u.len()).unwrap_or(0)
}
/// Print the current stack of the Hash Builder.
@ -326,8 +370,8 @@ impl HashBuilder {
// other side of the HashBuilder
tracing::debug!(target: "trie::hash_builder", node = ?n, "intermediate node");
let common_prefix = current.slice(0, len);
if let Some(tx) = &self.branch_node_sender {
let _ = tx.send((common_prefix, n));
if let Some(nodes) = self.updated_branch_nodes.as_mut() {
nodes.insert(common_prefix, n);
}
}
}
@ -429,8 +473,7 @@ mod tests {
#[test]
fn test_generates_branch_node() {
let (sender, recv) = mpsc::channel();
let mut hb = HashBuilder::new(Some(sender));
let mut hb = HashBuilder::default().with_updates(true);
// We have 1 branch node update to be stored at 0x01, indicated by the first nibble.
// That branch root node has 2 branch node children present at 0x1 and 0x2.
@ -477,11 +520,9 @@ mod tests {
hb.add_leaf(nibbles, val.as_ref());
});
let root = hb.root();
drop(hb);
let updates = recv.iter().collect::<Vec<_>>();
let (_, updates) = hb.split();
let updates = updates.iter().cloned().collect::<BTreeMap<_, _>>();
let update = updates.get(&Nibbles::from(hex!("01").as_slice())).unwrap();
assert_eq!(update.state_mask, TrieMask::new(0b1111)); // 1st nibble: 0, 1, 2, 3
assert_eq!(update.tree_mask, TrieMask::new(0));

View File

@ -1,40 +0,0 @@
use reth_primitives::H256;
#[derive(Clone)]
pub(crate) enum HashBuilderValue {
Bytes(Vec<u8>),
Hash(H256),
}
impl std::fmt::Debug for HashBuilderValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bytes(bytes) => write!(f, "Bytes({:?})", hex::encode(bytes)),
Self::Hash(hash) => write!(f, "Hash({:?})", hash),
}
}
}
impl From<Vec<u8>> for HashBuilderValue {
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}
impl From<&[u8]> for HashBuilderValue {
fn from(value: &[u8]) -> Self {
Self::Bytes(value.to_vec())
}
}
impl From<H256> for HashBuilderValue {
fn from(value: H256) -> Self {
Self::Hash(value)
}
}
impl Default for HashBuilderValue {
fn default() -> Self {
Self::Bytes(vec![])
}
}

View File

@ -36,7 +36,14 @@ pub use errors::{StateRootError, StorageRootError};
/// The implementation of the Merkle Patricia Trie.
mod trie;
pub use trie::{BranchNodeUpdate, BranchNodeUpdateSender, StateRoot, StorageRoot};
pub use trie::{StateRoot, StorageRoot};
/// Buffer for trie updates.
pub mod updates;
/// Utilities for state root checkpoint progress.
mod progress;
pub use progress::{IntermediateStateRootState, StateRootProgress};
/// Collection of trie-related test utilities.
#[cfg(any(test, feature = "test-utils"))]

View File

@ -11,7 +11,7 @@ use reth_db::{
use reth_primitives::{keccak256, BlockNumber, StorageEntry, H256};
use std::{collections::HashMap, ops::RangeInclusive};
/// A wrapper around a database transaction that loads prefix sets within a given transition range.
/// A wrapper around a database transaction that loads prefix sets within a given block range.
#[derive(Deref)]
pub struct PrefixSetLoader<'a, TX>(&'a TX);
@ -26,7 +26,7 @@ impl<'a, 'b, TX> PrefixSetLoader<'a, TX>
where
TX: DbTx<'b>,
{
/// Load all account and storage changes for the given transition id range.
/// Load all account and storage changes for the given block range.
pub fn load(
self,
range: RangeInclusive<BlockNumber>,

View File

@ -0,0 +1,47 @@
use crate::{cursor::CursorSubNode, hash_builder::HashBuilder, updates::TrieUpdates, Nibbles};
use reth_primitives::{trie::StoredSubNode, MerkleCheckpoint, H256};
/// The progress of the state root computation.
#[derive(Debug)]
pub enum StateRootProgress {
/// The complete state root computation with updates and computed root.
Complete(H256, TrieUpdates),
/// The intermediate progress of state root computation.
/// Contains the walker stack, the hash builder and the trie updates.
Progress(IntermediateStateRootState, TrieUpdates),
}
/// The intermediate state of the state root computation.
#[derive(Debug)]
pub struct IntermediateStateRootState {
/// Previously constructed hash builder.
pub hash_builder: HashBuilder,
/// Previously recorded walker stack.
pub walker_stack: Vec<CursorSubNode>,
/// The last hashed account key processed.
pub last_account_key: H256,
/// The last walker key processed.
pub last_walker_key: Nibbles,
}
impl From<IntermediateStateRootState> for MerkleCheckpoint {
fn from(value: IntermediateStateRootState) -> Self {
Self {
last_account_key: value.last_account_key,
last_walker_key: value.last_walker_key.hex_data,
walker_stack: value.walker_stack.into_iter().map(StoredSubNode::from).collect(),
state: value.hash_builder.into(),
}
}
}
impl From<MerkleCheckpoint> for IntermediateStateRootState {
fn from(value: MerkleCheckpoint) -> Self {
Self {
hash_builder: HashBuilder::from(value.state),
walker_stack: value.walker_stack.into_iter().map(CursorSubNode::from).collect(),
last_account_key: value.last_account_key,
last_walker_key: Nibbles::from(value.last_walker_key),
}
}
}

File diff suppressed because it is too large Load Diff

151
crates/trie/src/updates.rs Normal file
View File

@ -0,0 +1,151 @@
use crate::Nibbles;
use derive_more::Deref;
use reth_db::{
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
tables,
transaction::{DbTx, DbTxMut},
};
use reth_primitives::{
trie::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey},
H256,
};
use std::collections::BTreeMap;
/// The key of a trie node.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum TrieKey {
/// A node in the account trie.
AccountNode(StoredNibbles),
/// A node in the storage trie.
StorageNode(H256, StoredNibblesSubKey),
/// Storage trie of an account.
StorageTrie(H256),
}
/// The operation to perform on the trie.
#[derive(Debug, Clone)]
pub enum TrieOp {
/// Delete the node entry.
Delete,
/// Update the node entry with the provided value.
Update(BranchNodeCompact),
}
impl TrieOp {
/// Returns `true` if the operation is an update.
pub fn is_update(&self) -> bool {
matches!(self, TrieOp::Update(..))
}
}
/// The aggregation of trie updates.
#[derive(Debug, Default, Clone, Deref)]
pub struct TrieUpdates {
trie_operations: BTreeMap<TrieKey, TrieOp>,
}
impl<const N: usize> From<[(TrieKey, TrieOp); N]> for TrieUpdates {
fn from(value: [(TrieKey, TrieOp); N]) -> Self {
Self { trie_operations: BTreeMap::from(value) }
}
}
impl TrieUpdates {
/// Schedule a delete operation on a trie key.
///
/// # Panics
///
/// If the key already exists and the operation is an update.
pub fn schedule_delete(&mut self, key: TrieKey) {
let existing = self.trie_operations.insert(key, TrieOp::Delete);
if let Some(op) = existing {
assert!(!op.is_update(), "Tried to delete a node that was already updated");
}
}
/// Append the updates to the current updates.
pub fn append(&mut self, other: &mut Self) {
self.trie_operations.append(&mut other.trie_operations);
}
/// Extend the updates with trie updates.
pub fn extend(&mut self, updates: impl Iterator<Item = (TrieKey, TrieOp)>) {
self.trie_operations.extend(updates);
}
/// Extend the updates with account trie updates.
pub fn extend_with_account_updates(&mut self, updates: BTreeMap<Nibbles, BranchNodeCompact>) {
self.extend(updates.into_iter().map(|(nibbles, node)| {
(TrieKey::AccountNode(nibbles.hex_data.into()), TrieOp::Update(node))
}));
}
/// Extend the updates with storage trie updates.
pub fn extend_with_storage_updates(
&mut self,
hashed_address: H256,
updates: BTreeMap<Nibbles, BranchNodeCompact>,
) {
self.extend(updates.into_iter().map(|(nibbles, node)| {
(TrieKey::StorageNode(hashed_address, nibbles.hex_data.into()), TrieOp::Update(node))
}));
}
/// Flush updates all aggregated updates to the database.
pub fn flush<'a, 'tx, TX>(self, tx: &'a TX) -> Result<(), reth_db::Error>
where
TX: DbTx<'tx> + DbTxMut<'tx>,
{
if self.trie_operations.is_empty() {
return Ok(())
}
let mut account_trie_cursor = tx.cursor_write::<tables::AccountsTrie>()?;
let mut storage_trie_cursor = tx.cursor_dup_write::<tables::StoragesTrie>()?;
for (key, operation) in self.trie_operations {
match key {
TrieKey::AccountNode(nibbles) => match operation {
TrieOp::Delete => {
if account_trie_cursor.seek_exact(nibbles)?.is_some() {
account_trie_cursor.delete_current()?;
}
}
TrieOp::Update(node) => {
if !nibbles.inner.is_empty() {
account_trie_cursor.upsert(nibbles, node)?;
}
}
},
TrieKey::StorageTrie(hashed_address) => match operation {
TrieOp::Delete => {
if storage_trie_cursor.seek_exact(hashed_address)?.is_some() {
storage_trie_cursor.delete_current_duplicates()?;
}
}
TrieOp::Update(..) => unreachable!("Cannot update full storage trie."),
},
TrieKey::StorageNode(hashed_address, nibbles) => {
if !nibbles.inner.is_empty() {
// Delete the old entry if it exists.
if storage_trie_cursor
.seek_by_key_subkey(hashed_address, nibbles.clone())?
.filter(|e| e.nibbles == nibbles)
.is_some()
{
storage_trie_cursor.delete_current()?;
}
// The operation is an update, insert new entry.
if let TrieOp::Update(node) = operation {
storage_trie_cursor
.upsert(hashed_address, StorageTrieEntry { nibbles, node })?;
}
}
}
};
}
Ok(())
}
}

View File

@ -1,6 +1,7 @@
use crate::{
cursor::{CursorSubNode, TrieCursor},
prefix_set::PrefixSet,
updates::TrieUpdates,
Nibbles,
};
use reth_db::{table::Key, Error};
@ -20,6 +21,8 @@ pub struct TrieWalker<'a, K, C> {
pub can_skip_current_node: bool,
/// A `PrefixSet` representing the changes to be applied to the trie.
pub changes: PrefixSet,
/// The trie updates to be applied to the trie.
trie_updates: Option<TrieUpdates>,
__phantom: PhantomData<K>,
}
@ -30,8 +33,9 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
let mut this = Self {
cursor,
changes,
can_skip_current_node: false,
stack: vec![CursorSubNode::default()],
can_skip_current_node: false,
trie_updates: None,
__phantom: PhantomData::default(),
};
@ -45,6 +49,39 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
this
}
/// Constructs a new TrieWalker from existing stack and a cursor.
pub fn from_stack(cursor: &'a mut C, stack: Vec<CursorSubNode>, changes: PrefixSet) -> Self {
let mut this = Self {
cursor,
changes,
stack,
can_skip_current_node: false,
trie_updates: None,
__phantom: PhantomData::default(),
};
this.update_skip_node();
this
}
/// Sets the flag whether the trie updates should be stored.
pub fn with_updates(mut self, retain_updates: bool) -> Self {
self.set_updates(retain_updates);
self
}
/// Sets the flag whether the trie updates should be stored.
pub fn set_updates(&mut self, retain_updates: bool) {
if retain_updates {
self.trie_updates = Some(TrieUpdates::default());
}
}
/// Split the walker into stack and trie updates.
pub fn split(mut self) -> (Vec<CursorSubNode>, TrieUpdates) {
let trie_updates = self.trie_updates.take();
(self.stack, trie_updates.unwrap_or_default())
}
/// Prints the current stack of trie nodes.
pub fn print_stack(&self) {
println!("====================== STACK ======================");
@ -54,6 +91,11 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
println!("====================== END STACK ======================\n");
}
/// The current length of the trie updates.
pub fn updates_len(&self) -> usize {
self.trie_updates.as_ref().map(|u| u.len()).unwrap_or(0)
}
/// Advances the walker to the next trie node and updates the skip node flag.
///
/// # Returns
@ -121,7 +163,9 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
// Delete the current node if it's included in the prefix set or it doesn't contain the root
// hash.
if !self.can_skip_current_node || nibble != -1 {
self.cursor.delete_current()?;
if let Some((updates, key)) = self.trie_updates.as_mut().zip(self.cursor.current()?) {
updates.schedule_delete(key);
}
}
Ok(())
@ -209,7 +253,10 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
mod tests {
use super::*;
use crate::cursor::{AccountTrieCursor, StorageTrieCursor};
use reth_db::{mdbx::test_utils::create_test_rw_db, tables, transaction::DbTxMut};
use reth_db::{
cursor::DbCursorRW, mdbx::test_utils::create_test_rw_db, tables, transaction::DbTxMut,
};
use reth_primitives::trie::StorageTrieEntry;
use reth_provider::Transaction;
#[test]
@ -237,26 +284,32 @@ mod tests {
let db = create_test_rw_db();
let tx = Transaction::new(db.as_ref()).unwrap();
let account_trie =
AccountTrieCursor::new(tx.cursor_write::<tables::AccountsTrie>().unwrap());
test_cursor(account_trie, &inputs, &expected);
let mut account_cursor = tx.cursor_write::<tables::AccountsTrie>().unwrap();
for (k, v) in &inputs {
account_cursor.upsert(k.clone().into(), v.clone()).unwrap();
}
let account_trie = AccountTrieCursor::new(account_cursor);
test_cursor(account_trie, &expected);
let storage_trie = StorageTrieCursor::new(
tx.cursor_dup_write::<tables::StoragesTrie>().unwrap(),
H256::random(),
);
test_cursor(storage_trie, &inputs, &expected);
let hashed_address = H256::random();
let mut storage_cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
for (k, v) in &inputs {
storage_cursor
.upsert(
hashed_address,
StorageTrieEntry { nibbles: k.clone().into(), node: v.clone() },
)
.unwrap();
}
let storage_trie = StorageTrieCursor::new(storage_cursor, hashed_address);
test_cursor(storage_trie, &expected);
}
fn test_cursor<K, T>(mut trie: T, inputs: &[(Vec<u8>, BranchNodeCompact)], expected: &[Vec<u8>])
fn test_cursor<K, T>(mut trie: T, expected: &[Vec<u8>])
where
K: Key + From<Vec<u8>>,
T: TrieCursor<K>,
{
for (k, v) in inputs {
trie.upsert(k.clone().into(), v.clone()).unwrap();
}
let mut walker = TrieWalker::new(&mut trie, Default::default());
assert!(walker.key().unwrap().is_empty());
@ -275,11 +328,7 @@ mod tests {
fn cursor_rootnode_with_changesets() {
let db = create_test_rw_db();
let tx = Transaction::new(db.as_ref()).unwrap();
let mut trie = StorageTrieCursor::new(
tx.cursor_dup_write::<tables::StoragesTrie>().unwrap(),
H256::random(),
);
let mut cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
let nodes = vec![
(
@ -306,10 +355,13 @@ mod tests {
),
];
let hashed_address = H256::random();
for (k, v) in nodes {
trie.upsert(k.into(), v).unwrap();
cursor.upsert(hashed_address, StorageTrieEntry { nibbles: k.into(), node: v }).unwrap();
}
let mut trie = StorageTrieCursor::new(cursor, hashed_address);
// No changes
let mut cursor = TrieWalker::new(&mut trie, Default::default());
assert_eq!(cursor.key(), Some(Nibbles::from(vec![]))); // root