Files
reth/crates/trie/db/src/state.rs
2024-08-01 13:19:19 +00:00

225 lines
8.1 KiB
Rust

use crate::PrefixSetLoader;
use reth_db_api::transaction::DbTx;
use reth_execution_errors::StateRootError;
use reth_primitives::{BlockNumber, B256};
use reth_trie::{
hashed_cursor::{DatabaseHashedCursorFactory, HashedPostStateCursorFactory},
trie_cursor::{DatabaseTrieCursorFactory, InMemoryTrieCursorFactory},
updates::TrieUpdates,
HashedPostState, StateRoot, StateRootProgress,
};
use std::ops::RangeInclusive;
use tracing::debug;
/// Extends [`StateRoot`] with operations specific for working with a database transaction.
pub trait DatabaseStateRoot<'a, TX>: Sized {
/// Create a new [`StateRoot`] instance.
fn from_tx(tx: &'a TX) -> Self;
/// Given a block number range, identifies all the accounts and storage keys that
/// have changed.
///
/// # Returns
///
/// An instance of state root calculator with account and storage prefixes loaded.
fn incremental_root_calculator(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<Self, StateRootError>;
/// Computes the state root of the trie with the changed account and storage prefixes and
/// existing trie nodes.
///
/// # Returns
///
/// The updated state root.
fn incremental_root(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<B256, StateRootError>;
/// Computes the state root of the trie with the changed account and storage prefixes and
/// existing trie nodes collecting updates in the process.
///
/// Ignores the threshold.
///
/// # Returns
///
/// The updated state root and the trie updates.
fn incremental_root_with_updates(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<(B256, TrieUpdates), StateRootError>;
/// Computes the state root of the trie with the changed account and storage prefixes and
/// existing trie nodes collecting updates in the process.
///
/// # Returns
///
/// The intermediate progress of state root computation.
fn incremental_root_with_progress(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<StateRootProgress, StateRootError>;
/// Calculate the state root for this [`HashedPostState`].
/// Internally, this method retrieves prefixsets and uses them
/// to calculate incremental state root.
///
/// # Example
///
/// ```
/// use reth_db::test_utils::create_test_rw_db;
/// use reth_db_api::database::Database;
/// use reth_primitives::{Account, U256};
/// use reth_trie::{updates::TrieUpdates, HashedPostState, StateRoot};
/// use reth_trie_db::DatabaseStateRoot;
///
/// // Initialize the database
/// let db = create_test_rw_db();
///
/// // Initialize hashed post state
/// let mut hashed_state = HashedPostState::default();
/// hashed_state.accounts.insert(
/// [0x11; 32].into(),
/// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
/// );
///
/// // Initialize intermediate nodes if any.
/// let intermediate_nodes = TrieUpdates::default();
///
/// // Calculate the state root
/// let tx = db.tx().expect("failed to create transaction");
/// let state_root = StateRoot::overlay_root(&tx, hashed_state, intermediate_nodes);
/// ```
///
/// # Returns
///
/// The state root for this [`HashedPostState`].
fn overlay_root(
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<B256, StateRootError>;
/// Calculates the state root for this [`HashedPostState`] and returns it alongside trie
/// updates. See [`Self::overlay_root`] for more info.
fn overlay_root_with_updates(
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<(B256, TrieUpdates), StateRootError>;
}
impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
for StateRoot<DatabaseTrieCursorFactory<'a, TX>, DatabaseHashedCursorFactory<'a, TX>>
{
fn from_tx(tx: &'a TX) -> Self {
Self::new(DatabaseTrieCursorFactory::new(tx), DatabaseHashedCursorFactory::new(tx))
}
fn incremental_root_calculator(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<Self, StateRootError> {
let loaded_prefix_sets = PrefixSetLoader::new(tx).load(range)?;
Ok(Self::from_tx(tx).with_prefix_sets(loaded_prefix_sets))
}
fn incremental_root(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<B256, StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root");
Self::incremental_root_calculator(tx, range)?.root()
}
fn incremental_root_with_updates(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<(B256, TrieUpdates), StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root");
Self::incremental_root_calculator(tx, range)?.root_with_updates()
}
fn incremental_root_with_progress(
tx: &'a TX,
range: RangeInclusive<BlockNumber>,
) -> Result<StateRootProgress, StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root with progress");
Self::incremental_root_calculator(tx, range)?.root_with_progress()
}
fn overlay_root(
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<B256, StateRootError> {
let prefix_sets = post_state.construct_prefix_sets().freeze();
let state_sorted = post_state.into_sorted();
let nodes_sorted = intermediate_nodes.into_sorted();
StateRoot::new(
InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
)
.with_prefix_sets(prefix_sets)
.root()
}
fn overlay_root_with_updates(
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<(B256, TrieUpdates), StateRootError> {
let prefix_sets = post_state.construct_prefix_sets().freeze();
let state_sorted = post_state.into_sorted();
let nodes_sorted = intermediate_nodes.into_sorted();
StateRoot::new(
InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
)
.with_prefix_sets(prefix_sets)
.root_with_updates()
}
}
#[cfg(test)]
mod tests {
use super::*;
use reth_db::test_utils::create_test_rw_db;
use reth_db_api::database::Database;
use reth_primitives::{hex, revm_primitives::AccountInfo, Address, U256};
use revm::db::BundleState;
use std::collections::HashMap;
#[test]
fn from_bundle_state_with_rayon() {
let address1 = Address::with_last_byte(1);
let address2 = Address::with_last_byte(2);
let slot1 = U256::from(1015);
let slot2 = U256::from(2015);
let account1 = AccountInfo { nonce: 1, ..Default::default() };
let account2 = AccountInfo { nonce: 2, ..Default::default() };
let bundle_state = BundleState::builder(2..=2)
.state_present_account_info(address1, account1)
.state_present_account_info(address2, account2)
.state_storage(address1, HashMap::from([(slot1, (U256::ZERO, U256::from(10)))]))
.state_storage(address2, HashMap::from([(slot2, (U256::ZERO, U256::from(20)))]))
.build();
assert_eq!(bundle_state.reverts.len(), 1);
let post_state = HashedPostState::from_bundle_state(&bundle_state.state);
assert_eq!(post_state.accounts.len(), 2);
assert_eq!(post_state.storages.len(), 2);
let db = create_test_rw_db();
let tx = db.tx().expect("failed to create transaction");
assert_eq!(
StateRoot::overlay_root(&tx, post_state, Default::default()).unwrap(),
hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd")
);
}
}