Skip to content

Commit

Permalink
feat(trie): SparseStateTrie::update_account (paradigmxyz#12954)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkrasiuk authored Nov 28, 2024
1 parent cbcf79a commit e9a6e45
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pub const TRIE_ACCOUNT_RLP_MAX_SIZE: usize = 110;
#[cfg(test)]
mod tests {
use super::*;
use crate::TrieAccount;
use alloy_primitives::{B256, U256};
use alloy_rlp::Encodable;
use reth_trie_common::TrieAccount;

#[test]
fn account_rlp_max_size() {
Expand Down
4 changes: 4 additions & 0 deletions crates/trie/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
/// The implementation of hash builder.
pub mod hash_builder;

/// Constants related to the trie computation.
mod constants;
pub use constants::*;

mod account;
pub use account::TrieAccount;

Expand Down
3 changes: 2 additions & 1 deletion crates/trie/sparse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ workspace = true

[dependencies]
# reth
reth-tracing.workspace = true
reth-primitives-traits.workspace = true
reth-trie-common.workspace = true
reth-tracing.workspace = true

# alloy
alloy-primitives.workspace = true
Expand Down
60 changes: 55 additions & 5 deletions crates/trie/sparse/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
use crate::{RevealedSparseTrie, SparseStateTrieError, SparseStateTrieResult, SparseTrie};
use crate::{
RevealedSparseTrie, SparseStateTrieError, SparseStateTrieResult, SparseTrie, SparseTrieError,
};
use alloy_primitives::{
map::{HashMap, HashSet},
Bytes, B256,
};
use alloy_rlp::Decodable;
use alloy_rlp::{Decodable, Encodable};
use reth_primitives_traits::Account;
use reth_trie_common::{
updates::{StorageTrieUpdates, TrieUpdates},
MultiProof, Nibbles, TrieNode,
MultiProof, Nibbles, TrieAccount, TrieNode, EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
};
use std::iter::Peekable;

/// Sparse state trie representing lazy-loaded Ethereum state trie.
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct SparseStateTrie {
retain_updates: bool,
/// Sparse account trie.
state: SparseTrie,
/// Sparse storage tries.
Expand All @@ -22,6 +24,23 @@ pub struct SparseStateTrie {
revealed: HashMap<B256, HashSet<B256>>,
/// Collection of addresses that had their storage tries wiped.
wiped_storages: HashSet<B256>,
/// Flag indicating whether trie updates should be retained.
retain_updates: bool,
/// Reusable buffer for RLP encoding of trie accounts.
account_rlp_buf: Vec<u8>,
}

impl Default for SparseStateTrie {
fn default() -> Self {
Self {
state: Default::default(),
storages: Default::default(),
revealed: Default::default(),
wiped_storages: Default::default(),
retain_updates: false,
account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
}
}
}

impl SparseStateTrie {
Expand Down Expand Up @@ -186,6 +205,37 @@ impl SparseStateTrie {
Ok(Some(root_node))
}

/// Update or remove trie account based on new account info. This method will either recompute
/// the storage root based on update storage trie or look it up from existing leaf value.
///
/// If the new account info and storage trie are empty, the account leaf will be removed.
pub fn update_account(&mut self, address: B256, account: Account) -> SparseStateTrieResult<()> {
let nibbles = Nibbles::unpack(address);
let storage_root = if let Some(storage_trie) = self.storages.get_mut(&address) {
storage_trie.root().ok_or(SparseTrieError::Blind)?
} else if self.revealed.contains_key(&address) {
let state = self.state.as_revealed_mut().ok_or(SparseTrieError::Blind)?;
// The account was revealed, either...
if let Some(value) = state.get_leaf_value(&nibbles) {
// ..it exists and we should take it's current storage root or...
TrieAccount::decode(&mut &value[..])?.storage_root
} else {
// ...the account is newly created and the storage trie is empty.
EMPTY_ROOT_HASH
}
} else {
return Err(SparseTrieError::Blind.into())
};

if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
self.remove_account_leaf(&nibbles)
} else {
self.account_rlp_buf.clear();
TrieAccount::from((account, storage_root)).encode(&mut self.account_rlp_buf);
self.update_account_leaf(nibbles, self.account_rlp_buf.clone())
}
}

/// Update the account leaf node.
pub fn update_account_leaf(
&mut self,
Expand Down
5 changes: 5 additions & 0 deletions crates/trie/sparse/src/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ impl RevealedSparseTrie {
self.updates.as_ref().map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
}

/// Returns a reference to the leaf value if present.
pub fn get_leaf_value(&self, path: &Nibbles) -> Option<&Vec<u8>> {
self.values.get(path)
}

/// Takes and returns the retained sparse node updates
pub fn take_updates(&mut self) -> SparseTrieUpdates {
self.updates.take().unwrap_or_default()
Expand Down
4 changes: 0 additions & 4 deletions crates/trie/trie/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

/// Constants related to the trie computation.
mod constants;
pub use constants::*;

/// The implementation of forward-only in-memory cursor.
pub mod forward_cursor;

Expand Down

0 comments on commit e9a6e45

Please sign in to comment.