feat(revm): Integrate State (#3512)

Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com>
Co-authored-by: Alexey Shekhirin <a.shekhirin@gmail.com>
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
Co-authored-by: Bjerg <onbjerg@users.noreply.github.com>
This commit is contained in:
rakita
2023-09-16 13:00:22 +02:00
committed by GitHub
parent 62e7d98202
commit f153d8f4d4
93 changed files with 3436 additions and 4975 deletions

View File

@ -27,16 +27,19 @@ tracing.workspace = true
auto_impl = "1.0"
itertools.workspace = true
pin-project.workspace = true
derive_more = "0.99"
parking_lot.workspace = true
# test-utils
reth-rlp = { workspace = true, optional = true }
# parallel utils
rayon = "1.7"
[dev-dependencies]
reth-db = { path = "../db", features = ["test-utils"] }
reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] }
reth-rlp.workspace = true
revm.workspace = true
reth-trie = { path = "../../trie", features = ["test-utils"] }
reth-interfaces = { workspace = true, features = ["test-utils"] }
parking_lot.workspace = true

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
//! Bundle state module.
//! This module contains all the logic related to bundle state.
mod bundle_state_with_receipts;
mod state_changes;
mod state_reverts;
pub use bundle_state_with_receipts::{
AccountRevertInit, BundleStateInit, BundleStateWithReceipts, OriginalValuesKnown, RevertsInit,
};
pub use state_changes::StateChanges;
pub use state_reverts::StateReverts;

View File

@ -0,0 +1,88 @@
use rayon::slice::ParallelSliceMut;
use reth_db::{
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
tables,
transaction::{DbTx, DbTxMut},
};
use reth_interfaces::db::DatabaseError;
use reth_primitives::{Bytecode, StorageEntry, U256};
use reth_revm_primitives::{
db::states::{PlainStorageChangeset, StateChangeset},
into_reth_acc,
};
/// A change to the state of the world.
#[derive(Default)]
pub struct StateChanges(pub StateChangeset);
impl From<StateChangeset> for StateChanges {
fn from(revm: StateChangeset) -> Self {
Self(revm)
}
}
impl StateChanges {
/// Write the post state to the database.
pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>(
mut self,
tx: &TX,
) -> Result<(), DatabaseError> {
// sort all entries so they can be written to database in more performant way.
// and take smaller memory footprint.
self.0.accounts.par_sort_by_key(|a| a.0);
self.0.storage.par_sort_by_key(|a| a.address);
self.0.contracts.par_sort_by_key(|a| a.0);
// Write new account state
tracing::trace!(target: "provider::post_state", len = self.0.accounts.len(), "Writing new account state");
let mut accounts_cursor = tx.cursor_write::<tables::PlainAccountState>()?;
// write account to database.
for (address, account) in self.0.accounts.into_iter() {
if let Some(account) = account {
tracing::trace!(target: "provider::post_state", ?address, "Updating plain state account");
accounts_cursor.upsert(address, into_reth_acc(account))?;
} else if accounts_cursor.seek_exact(address)?.is_some() {
tracing::trace!(target: "provider::post_state", ?address, "Deleting plain state account");
accounts_cursor.delete_current()?;
}
}
// Write bytecode
tracing::trace!(target: "provider::post_state", len = self.0.contracts.len(), "Writing bytecodes");
let mut bytecodes_cursor = tx.cursor_write::<tables::Bytecodes>()?;
for (hash, bytecode) in self.0.contracts.into_iter() {
bytecodes_cursor.upsert(hash, Bytecode(bytecode))?;
}
// Write new storage state and wipe storage if needed.
tracing::trace!(target: "provider::post_state", len = self.0.storage.len(), "Writing new storage state");
let mut storages_cursor = tx.cursor_dup_write::<tables::PlainStorageState>()?;
for PlainStorageChangeset { address, wipe_storage, storage } in self.0.storage.into_iter() {
// Wiping of storage.
if wipe_storage && storages_cursor.seek_exact(address)?.is_some() {
storages_cursor.delete_current_duplicates()?;
}
// cast storages to H256.
let mut storage = storage
.into_iter()
.map(|(k, value)| StorageEntry { key: k.into(), value })
.collect::<Vec<_>>();
// sort storage slots by key.
storage.par_sort_unstable_by_key(|a| a.key);
for entry in storage.into_iter() {
tracing::trace!(target: "provider::post_state", ?address, ?entry.key, "Updating plain state storage");
if let Some(db_entry) = storages_cursor.seek_by_key_subkey(address, entry.key)? {
if db_entry.key == entry.key {
storages_cursor.delete_current()?;
}
}
if entry.value != U256::ZERO {
storages_cursor.upsert(address, entry)?;
}
}
}
Ok(())
}
}

View File

@ -0,0 +1,167 @@
use rayon::slice::ParallelSliceMut;
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO, DbDupCursorRW},
models::{AccountBeforeTx, BlockNumberAddress},
tables,
transaction::{DbTx, DbTxMut},
};
use reth_interfaces::db::DatabaseError;
use reth_primitives::{BlockNumber, StorageEntry, H256, U256};
use reth_revm_primitives::{
db::states::{PlainStateReverts, PlainStorageRevert, RevertToSlot},
into_reth_acc,
};
use std::iter::Peekable;
/// Revert of the state.
#[derive(Default)]
pub struct StateReverts(pub PlainStateReverts);
impl From<PlainStateReverts> for StateReverts {
fn from(revm: PlainStateReverts) -> Self {
Self(revm)
}
}
impl StateReverts {
/// Write reverts to database.
///
/// Note:: Reverts will delete all wiped storage from plain state.
pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>(
self,
tx: &TX,
first_block: BlockNumber,
) -> Result<(), DatabaseError> {
// Write storage changes
tracing::trace!(target: "provider::reverts", "Writing storage changes");
let mut storages_cursor = tx.cursor_dup_write::<tables::PlainStorageState>()?;
let mut storage_changeset_cursor = tx.cursor_dup_write::<tables::StorageChangeSet>()?;
for (block_index, mut storage_changes) in self.0.storage.into_iter().enumerate() {
let block_number = first_block + block_index as BlockNumber;
tracing::trace!(target: "provider::reverts", block_number, "Writing block change");
// sort changes by address.
storage_changes.par_sort_unstable_by_key(|a| a.address);
for PlainStorageRevert { address, wiped, storage_revert } in storage_changes.into_iter()
{
let storage_id = BlockNumberAddress((block_number, address));
let mut storage = storage_revert
.into_iter()
.map(|(k, v)| (H256(k.to_be_bytes()), v))
.collect::<Vec<_>>();
// sort storage slots by key.
storage.par_sort_unstable_by_key(|a| a.0);
// If we are writing the primary storage wipe transition, the pre-existing plain
// storage state has to be taken from the database and written to storage history.
// See [StorageWipe::Primary] for more details.
let mut wiped_storage = Vec::new();
if wiped {
tracing::trace!(target: "provider::reverts", ?address, "Wiping storage");
if let Some((_, entry)) = storages_cursor.seek_exact(address)? {
wiped_storage.push((entry.key, entry.value));
while let Some(entry) = storages_cursor.next_dup_val()? {
wiped_storage.push((entry.key, entry.value))
}
}
}
tracing::trace!(target: "provider::reverts", ?address, ?storage, "Writing storage reverts");
for (key, value) in StorageRevertsIter::new(storage, wiped_storage) {
storage_changeset_cursor.append_dup(storage_id, StorageEntry { key, value })?;
}
}
}
// Write account changes
tracing::trace!(target: "provider::reverts", "Writing account changes");
let mut account_changeset_cursor = tx.cursor_dup_write::<tables::AccountChangeSet>()?;
for (block_index, mut account_block_reverts) in self.0.accounts.into_iter().enumerate() {
let block_number = first_block + block_index as BlockNumber;
// Sort accounts by address.
account_block_reverts.par_sort_by_key(|a| a.0);
for (address, info) in account_block_reverts {
account_changeset_cursor.append_dup(
block_number,
AccountBeforeTx { address, info: info.map(into_reth_acc) },
)?;
}
}
Ok(())
}
}
/// Iterator over storage reverts.
/// See [StorageRevertsIter::next] for more details.
struct StorageRevertsIter<R: Iterator, W: Iterator> {
reverts: Peekable<R>,
wiped: Peekable<W>,
}
impl<R: Iterator, W: Iterator> StorageRevertsIter<R, W>
where
R: Iterator<Item = (H256, RevertToSlot)>,
W: Iterator<Item = (H256, U256)>,
{
fn new(
reverts: impl IntoIterator<IntoIter = R>,
wiped: impl IntoIterator<IntoIter = W>,
) -> Self {
Self { reverts: reverts.into_iter().peekable(), wiped: wiped.into_iter().peekable() }
}
/// Consume next revert and return it.
fn next_revert(&mut self) -> Option<(H256, U256)> {
self.reverts.next().map(|(key, revert)| (key, revert.to_previous_value()))
}
/// Consume next wiped storage and return it.
fn next_wiped(&mut self) -> Option<(H256, U256)> {
self.wiped.next()
}
}
impl<R, W> Iterator for StorageRevertsIter<R, W>
where
R: Iterator<Item = (H256, RevertToSlot)>,
W: Iterator<Item = (H256, U256)>,
{
type Item = (H256, U256);
/// Iterate over storage reverts and wiped entries and return items in the sorted order.
/// NOTE: The implementation assumes that inner iterators are already sorted.
fn next(&mut self) -> Option<Self::Item> {
match (self.reverts.peek(), self.wiped.peek()) {
(Some(revert), Some(wiped)) => {
// Compare the keys and return the lesser.
use std::cmp::Ordering;
match revert.0.cmp(&wiped.0) {
Ordering::Less => self.next_revert(),
Ordering::Greater => self.next_wiped(),
Ordering::Equal => {
// Keys are the same, decide which one to return.
let (key, revert_to) = *revert;
let value = match revert_to {
// If the slot is some, prefer the revert value.
RevertToSlot::Some(value) => value,
// If the slot was destroyed, prefer the database value.
RevertToSlot::Destroyed => wiped.1,
};
// Consume both values from inner iterators.
self.next_revert();
self.next_wiped();
Some((key, value))
}
}
}
(Some(_revert), None) => self.next_revert(),
(None, Some(_wiped)) => self.next_wiped(),
(None, None) => None,
}
}
}

View File

@ -1,6 +1,6 @@
//! Contains [Chain], a chain of blocks and their final state.
use crate::PostState;
use crate::bundle_state::BundleStateWithReceipts;
use reth_interfaces::{executor::BlockExecutionError, Error};
use reth_primitives::{
BlockHash, BlockNumHash, BlockNumber, ForkBlock, Receipt, SealedBlock, SealedBlockWithSenders,
@ -20,7 +20,7 @@ pub struct Chain {
/// [Chain::first] to [Chain::tip], inclusive.
///
/// This state also contains the individual changes that lead to the current state.
pub state: PostState,
pub state: BundleStateWithReceipts,
/// All blocks in this chain.
pub blocks: BTreeMap<BlockNumber, SealedBlockWithSenders>,
}
@ -42,7 +42,7 @@ impl Chain {
}
/// Get post state of this chain
pub fn state(&self) -> &PostState {
pub fn state(&self) -> &BundleStateWithReceipts {
&self.state
}
@ -64,7 +64,7 @@ impl Chain {
}
/// Return post state of the block at the `block_number` or None if block is not known
pub fn state_at_block(&self, block_number: BlockNumber) -> Option<PostState> {
pub fn state_at_block(&self, block_number: BlockNumber) -> Option<BundleStateWithReceipts> {
if self.tip().number == block_number {
return Some(self.state.clone())
}
@ -79,13 +79,13 @@ impl Chain {
/// Destructure the chain into its inner components, the blocks and the state at the tip of the
/// chain.
pub fn into_inner(self) -> (ChainBlocks<'static>, PostState) {
pub fn into_inner(self) -> (ChainBlocks<'static>, BundleStateWithReceipts) {
(ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.state)
}
/// Destructure the chain into its inner components, the blocks and the state at the tip of the
/// chain.
pub fn inner(&self) -> (ChainBlocks<'_>, &PostState) {
pub fn inner(&self) -> (ChainBlocks<'_>, &BundleStateWithReceipts) {
(ChainBlocks { blocks: Cow::Borrowed(&self.blocks) }, &self.state)
}
@ -125,15 +125,8 @@ impl Chain {
}
/// Create new chain with given blocks and post state.
pub fn new(blocks: Vec<(SealedBlockWithSenders, PostState)>) -> Self {
let mut state = PostState::default();
let mut block_num_hash = BTreeMap::new();
for (block, block_state) in blocks.into_iter() {
state.extend(block_state);
block_num_hash.insert(block.number, block);
}
Self { state, blocks: block_num_hash }
pub fn new(blocks: Vec<SealedBlockWithSenders>, state: BundleStateWithReceipts) -> Self {
Self { state, blocks: blocks.into_iter().map(|b| (b.number, b)).collect() }
}
/// Returns length of the chain.
@ -142,9 +135,9 @@ impl Chain {
}
/// Get all receipts for the given block.
pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<&[Receipt]> {
pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<Vec<&Receipt>> {
let num = self.block_number(block_hash)?;
Some(self.state.receipts(num))
self.state.receipts_by_block(num).iter().map(Option::as_ref).collect()
}
/// Get all receipts with attachment.
@ -152,13 +145,14 @@ impl Chain {
/// Attachment includes block number, block hash, transaction hash and transaction index.
pub fn receipts_with_attachment(&self) -> Vec<BlockReceipts> {
let mut receipt_attch = Vec::new();
for (block_num, block) in self.blocks().iter() {
let mut receipts = self.state.receipts(*block_num).iter();
for ((block_num, block), receipts) in self.blocks().iter().zip(self.state.receipts().iter())
{
let mut tx_receipts = Vec::new();
for tx in block.body.iter() {
if let Some(receipt) = receipts.next() {
tx_receipts.push((tx.hash(), receipt.clone()));
}
for (tx, receipt) in block.body.iter().zip(receipts.iter()) {
tx_receipts.push((
tx.hash(),
receipt.as_ref().expect("receipts have not been pruned").clone(),
));
}
let block_num_hash = BlockNumHash::new(*block_num, block.hash());
receipt_attch.push(BlockReceipts { block: block_num_hash, tx_receipts });
@ -188,7 +182,7 @@ impl Chain {
/// Split this chain at the given block.
///
/// The given block will be the first block in the first returned chain.
/// The given block will be the last block in the first returned chain.
///
/// If the given block is not found, [`ChainSplit::NoSplitPending`] is returned.
/// Split chain at the number or hash, block with given number will be included at first chain.
@ -196,7 +190,7 @@ impl Chain {
///
/// # Note
///
/// The block number to transition ID mapping is only found in the second chain, making it
/// The plain state is only found in the second chain, making it
/// impossible to perform any state reverts on the first chain.
///
/// The second chain only contains the changes that were reverted on the first chain; however,
@ -229,13 +223,13 @@ impl Chain {
let higher_number_blocks = self.blocks.split_off(&(block_number + 1));
let mut canonical_state = std::mem::take(&mut self.state);
let new_state = canonical_state.split_at(block_number);
self.state = new_state;
let mut state = std::mem::take(&mut self.state);
let canonical_state =
state.split_at(block_number).expect("Detach block number to be in range");
ChainSplit::Split {
canonical: Chain { state: canonical_state, blocks: self.blocks },
pending: Chain { state: self.state, blocks: higher_number_blocks },
pending: Chain { state, blocks: higher_number_blocks },
}
}
}
@ -365,7 +359,11 @@ pub enum ChainSplit {
#[cfg(test)]
mod tests {
use super::*;
use reth_primitives::{Account, H160, H256};
use reth_primitives::{H160, H256};
use reth_revm_primitives::{
db::BundleState,
primitives::{AccountInfo, HashMap},
};
#[test]
fn chain_append() {
@ -401,15 +399,25 @@ mod tests {
#[test]
fn test_number_split() {
let mut base_state = PostState::default();
let account = Account { nonce: 10, ..Default::default() };
base_state.create_account(1, H160([1; 20]), account);
let block_state1 = BundleStateWithReceipts::new(
BundleState::new(
vec![(H160([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
vec![vec![(H160([2; 20]), None, vec![])]],
vec![],
),
vec![vec![]],
1,
);
let mut block_state1 = PostState::default();
block_state1.create_account(2, H160([2; 20]), Account::default());
let mut block_state2 = PostState::default();
block_state2.create_account(3, H160([3; 20]), Account::default());
let block_state2 = BundleStateWithReceipts::new(
BundleState::new(
vec![(H160([3; 20]), None, Some(AccountInfo::default()), HashMap::default())],
vec![vec![(H160([3; 20]), None, vec![])]],
vec![],
),
vec![vec![]],
2,
);
let mut block1 = SealedBlockWithSenders::default();
let block1_hash = H256([15; 32]);
@ -423,13 +431,13 @@ mod tests {
block2.hash = block2_hash;
block2.senders.push(H160([4; 20]));
let chain = Chain::new(vec![
(block1.clone(), block_state1.clone()),
(block2.clone(), block_state2.clone()),
]);
let mut block_state_extended = block_state1.clone();
block_state_extended.extend(block_state2.clone());
let mut split1_state = chain.state.clone();
let split2_state = split1_state.split_at(1);
let chain = Chain::new(vec![block1.clone(), block2.clone()], block_state_extended);
let mut split2_state = chain.state.clone();
let split1_state = split2_state.split_at(1).unwrap();
let chain_split1 =
Chain { state: split1_state, blocks: BTreeMap::from([(1, block1.clone())]) };

View File

@ -21,12 +21,12 @@
/// Various provider traits.
mod traits;
pub use traits::{
AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockHashReader,
BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, BlockWriter,
BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotification,
CanonStateNotificationSender, CanonStateNotifications, CanonStateSubscriptions,
ChainSpecProvider, ChangeSetReader, EvmEnvProvider, ExecutorFactory, HashingWriter,
HeaderProvider, HistoryWriter, PostStateDataProvider, PruneCheckpointReader,
AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockExecutorStats,
BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource,
BlockWriter, BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker,
CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications,
CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, ExecutorFactory,
HashingWriter, HeaderProvider, HistoryWriter, PrunableBlockExecutor, PruneCheckpointReader,
PruneCheckpointWriter, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader,
StageCheckpointWriter, StateProvider, StateProviderBox, StateProviderFactory,
StateRootProvider, StorageReader, TransactionsProvider, WithdrawalsProvider,
@ -39,10 +39,6 @@ pub use providers::{
HistoricalStateProviderRef, LatestStateProvider, LatestStateProviderRef, ProviderFactory,
};
/// Execution result
pub mod post_state;
pub use post_state::PostState;
#[cfg(any(test, feature = "test-utils"))]
/// Common test helpers for mocking the Provider.
pub mod test_utils;
@ -52,3 +48,6 @@ pub use reth_interfaces::provider::ProviderError;
pub mod chain;
pub use chain::{Chain, DisplayBlocksChain};
pub mod bundle_state;
pub use bundle_state::{BundleStateWithReceipts, OriginalValuesKnown, StateChanges, StateReverts};

View File

@ -1,89 +0,0 @@
use derive_more::Deref;
use reth_primitives::{Account, Address, BlockNumber};
use std::collections::{btree_map::Entry, BTreeMap};
/// A mapping of `block -> address -> account` that represents what accounts were changed, and what
/// their state were prior to that change.
///
/// If the prior state was `None`, then the account is new.
#[derive(Default, Clone, Eq, PartialEq, Debug, Deref)]
pub struct AccountChanges {
/// The inner mapping of block changes.
#[deref]
pub inner: BTreeMap<BlockNumber, BTreeMap<Address, Option<Account>>>,
/// Hand tracked change size.
pub size: usize,
}
impl AccountChanges {
/// Insert account change at specified block number. The value is **not** updated if it already
/// exists.
pub fn insert(
&mut self,
block: BlockNumber,
address: Address,
old: Option<Account>,
new: Option<Account>,
) {
match self.inner.entry(block).or_default().entry(address) {
Entry::Vacant(entry) => {
self.size += 1;
entry.insert(old);
}
Entry::Occupied(entry) => {
// If the account state is the same before and after this block, collapse the state
// changes.
if entry.get() == &new {
entry.remove();
self.size -= 1;
}
}
}
}
/// Insert account changes at specified block number. The values are **not** updated if they
/// already exist.
pub fn insert_for_block(
&mut self,
block: BlockNumber,
changes: BTreeMap<Address, Option<Account>>,
) {
let block_entry = self.inner.entry(block).or_default();
for (address, account) in changes {
if let Entry::Vacant(entry) = block_entry.entry(address) {
entry.insert(account);
self.size += 1;
}
}
}
/// Drain and return any entries above the target block number.
pub fn drain_above(
&mut self,
target_block: BlockNumber,
) -> BTreeMap<BlockNumber, BTreeMap<Address, Option<Account>>> {
let mut evicted = BTreeMap::new();
self.inner.retain(|block_number, accounts| {
if *block_number > target_block {
self.size -= accounts.len();
evicted.insert(*block_number, accounts.clone());
false
} else {
true
}
});
evicted
}
/// Retain entries only above specified block number.
pub fn retain_above(&mut self, target_block: BlockNumber) {
self.inner.retain(|block_number, accounts| {
if *block_number > target_block {
true
} else {
self.size -= accounts.len();
false
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +0,0 @@
use derive_more::Deref;
use reth_primitives::{Address, BlockNumber, U256};
use std::collections::{btree_map::Entry, BTreeMap};
/// Storage for an account with the old and new values for each slot: (slot -> (old, new)).
pub type StorageChangeset = BTreeMap<U256, (U256, U256)>;
/// The storage state of the account before the state transition.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct StorageTransition {
/// The indicator of the storage wipe.
pub wipe: StorageWipe,
/// The storage slots.
pub storage: BTreeMap<U256, U256>,
}
/// The indicator of the storage wipe.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub enum StorageWipe {
/// The storage was not wiped at this change.
#[default]
None,
/// The storage was wiped for the first time in the current in-memory state.
///
/// When writing history to the database, on the primary storage wipe the pre-existing storage
/// will be inserted as the storage state before this transition.
Primary,
/// The storage had been already wiped before.
Secondary,
}
impl StorageWipe {
/// Returns `true` if the wipe occurred at this transition.
pub fn is_wiped(&self) -> bool {
matches!(self, Self::Primary | Self::Secondary)
}
/// Returns `true` if the primary wiped occurred at this transition.
/// See [StorageWipe::Primary] for more details.
pub fn is_primary(&self) -> bool {
matches!(self, Self::Primary)
}
}
/// Latest storage state for the account.
///
/// # Wiped Storage
///
/// The `times_wiped` field indicates the number of times the storage was wiped in this poststate.
///
/// If `times_wiped` is greater than 0, then the account was selfdestructed at some point, and the
/// values contained in `storage` should be the only values written to the database.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Storage {
/// The number of times the storage was wiped.
pub times_wiped: u64,
/// The storage slots.
pub storage: BTreeMap<U256, U256>,
}
impl Storage {
/// Returns `true` if the storage was wiped at any point.
pub fn wiped(&self) -> bool {
self.times_wiped > 0
}
}
/// A mapping of `block -> account -> slot -> old value` that represents what slots were changed,
/// and what their values were prior to that change.
#[derive(Default, Clone, Eq, PartialEq, Debug, Deref)]
pub struct StorageChanges {
/// The inner mapping of block changes.
#[deref]
pub inner: BTreeMap<BlockNumber, BTreeMap<Address, StorageTransition>>,
/// Hand tracked change size.
pub size: usize,
}
impl StorageChanges {
/// Insert storage entries for specified block number and address.
pub fn insert_for_block_and_address<I>(
&mut self,
block: BlockNumber,
address: Address,
wipe: StorageWipe,
storage: I,
) where
I: Iterator<Item = (U256, U256)>,
{
let block_entry = self.inner.entry(block).or_default();
let storage_entry = block_entry.entry(address).or_default();
if wipe.is_wiped() {
storage_entry.wipe = wipe;
}
for (slot, value) in storage {
if let Entry::Vacant(entry) = storage_entry.storage.entry(slot) {
entry.insert(value);
self.size += 1;
}
}
}
/// Drain and return any entries above the target block number.
pub fn drain_above(
&mut self,
target_block: BlockNumber,
) -> BTreeMap<BlockNumber, BTreeMap<Address, StorageTransition>> {
let mut evicted = BTreeMap::new();
self.inner.retain(|block_number, storages| {
if *block_number > target_block {
// This is fine, because it's called only on post state splits
self.size -=
storages.iter().fold(0, |acc, (_, storage)| acc + storage.storage.len());
evicted.insert(*block_number, storages.clone());
false
} else {
true
}
});
evicted
}
/// Retain entries only above specified block number.
///
/// # Returns
///
/// The update mapping of address to the number of times it was wiped.
pub fn retain_above(&mut self, target_block: BlockNumber) -> BTreeMap<Address, u64> {
let mut updated_times_wiped: BTreeMap<Address, u64> = BTreeMap::default();
self.inner.retain(|block_number, storages| {
if *block_number > target_block {
for (address, storage) in storages.iter_mut() {
if storage.wipe.is_wiped() {
let times_wiped_entry = updated_times_wiped.entry(*address).or_default();
storage.wipe = if *times_wiped_entry == 0 {
// No wipe was observed, promote the wipe to primary even if it was
// secondary before.
StorageWipe::Primary
} else {
// We already observed the storage wipe for this address
StorageWipe::Secondary
};
*times_wiped_entry += 1;
}
}
true
} else {
// This is fine, because it's called only on post state splits
self.size -=
storages.iter().fold(0, |acc, (_, storage)| acc + storage.storage.len());
false
}
});
updated_times_wiped
}
}

View File

@ -1,30 +1,30 @@
use crate::{
AccountReader, BlockHashReader, PostState, PostStateDataProvider, StateProvider,
StateRootProvider,
bundle_state::BundleStateWithReceipts, AccountReader, BlockHashReader, BundleStateDataProvider,
StateProvider, StateRootProvider,
};
use reth_interfaces::{provider::ProviderError, Result};
use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, H256, U256};
use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, H256};
/// A state provider that either resolves to data in a wrapped [`crate::PostState`], or an
/// underlying state provider.
pub struct PostStateProvider<SP: StateProvider, PSDP: PostStateDataProvider> {
/// A state provider that either resolves to data in a wrapped [`crate::BundleStateWithReceipts`],
/// or an underlying state provider.
pub struct BundleStateProvider<SP: StateProvider, BSDP: BundleStateDataProvider> {
/// The inner state provider.
pub(crate) state_provider: SP,
/// Post state data,
pub(crate) post_state_data_provider: PSDP,
pub(crate) post_state_data_provider: BSDP,
}
impl<SP: StateProvider, PSDP: PostStateDataProvider> PostStateProvider<SP, PSDP> {
impl<SP: StateProvider, BSDP: BundleStateDataProvider> BundleStateProvider<SP, BSDP> {
/// Create new post-state provider
pub fn new(state_provider: SP, post_state_data_provider: PSDP) -> Self {
pub fn new(state_provider: SP, post_state_data_provider: BSDP) -> Self {
Self { state_provider, post_state_data_provider }
}
}
/* Implement StateProvider traits */
impl<SP: StateProvider, PSDP: PostStateDataProvider> BlockHashReader
for PostStateProvider<SP, PSDP>
impl<SP: StateProvider, BSDP: BundleStateDataProvider> BlockHashReader
for BundleStateProvider<SP, BSDP>
{
fn block_hash(&self, block_number: BlockNumber) -> Result<Option<H256>> {
let block_hash = self.post_state_data_provider.block_hash(block_number);
@ -39,48 +39,48 @@ impl<SP: StateProvider, PSDP: PostStateDataProvider> BlockHashReader
}
}
impl<SP: StateProvider, PSDP: PostStateDataProvider> AccountReader for PostStateProvider<SP, PSDP> {
impl<SP: StateProvider, BSDP: BundleStateDataProvider> AccountReader
for BundleStateProvider<SP, BSDP>
{
fn basic_account(&self, address: Address) -> Result<Option<Account>> {
if let Some(account) = self.post_state_data_provider.state().account(&address) {
Ok(*account)
Ok(account)
} else {
self.state_provider.basic_account(address)
}
}
}
impl<SP: StateProvider, PSDP: PostStateDataProvider> StateRootProvider
for PostStateProvider<SP, PSDP>
impl<SP: StateProvider, BSDP: BundleStateDataProvider> StateRootProvider
for BundleStateProvider<SP, BSDP>
{
fn state_root(&self, post_state: PostState) -> Result<H256> {
fn state_root(&self, post_state: BundleStateWithReceipts) -> Result<H256> {
let mut state = self.post_state_data_provider.state().clone();
state.extend(post_state);
self.state_provider.state_root(state)
}
}
impl<SP: StateProvider, PSDP: PostStateDataProvider> StateProvider for PostStateProvider<SP, PSDP> {
impl<SP: StateProvider, BSDP: BundleStateDataProvider> StateProvider
for BundleStateProvider<SP, BSDP>
{
fn storage(
&self,
account: Address,
storage_key: reth_primitives::StorageKey,
) -> Result<Option<reth_primitives::StorageValue>> {
if let Some(storage) = self.post_state_data_provider.state().account_storage(&account) {
if let Some(value) =
storage.storage.get(&U256::from_be_bytes(storage_key.to_fixed_bytes()))
{
return Ok(Some(*value))
} else if storage.wiped() {
return Ok(Some(U256::ZERO))
}
let u256_storage_key = storage_key.into();
if let Some(value) =
self.post_state_data_provider.state().storage(&account, u256_storage_key)
{
return Ok(Some(value))
}
self.state_provider.storage(account, storage_key)
}
fn bytecode_by_hash(&self, code_hash: H256) -> Result<Option<Bytecode>> {
if let Some(bytecode) = self.post_state_data_provider.state().bytecode(&code_hash).cloned()
{
if let Some(bytecode) = self.post_state_data_provider.state().bytecode(&code_hash) {
return Ok(Some(bytecode))
}

View File

@ -1,12 +1,12 @@
use crate::{
post_state::StorageChangeset,
bundle_state::{BundleStateInit, BundleStateWithReceipts, RevertsInit},
traits::{
AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter,
},
AccountReader, BlockExecutionWriter, BlockHashReader, BlockNumReader, BlockReader, BlockWriter,
EvmEnvProvider, HashingWriter, HeaderProvider, HistoryWriter, PostState, ProviderError,
PruneCheckpointReader, PruneCheckpointWriter, StageCheckpointReader, StorageReader,
TransactionsProvider, WithdrawalsProvider,
Chain, EvmEnvProvider, HashingWriter, HeaderProvider, HistoryWriter, OriginalValuesKnown,
ProviderError, PruneCheckpointReader, PruneCheckpointWriter, StageCheckpointReader,
StorageReader, TransactionsProvider, WithdrawalsProvider,
};
use itertools::{izip, Itertools};
use reth_db::{
@ -43,7 +43,7 @@ use reth_revm_primitives::{
};
use reth_trie::{prefix_set::PrefixSetMut, StateRoot};
use std::{
collections::{btree_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet},
fmt::Debug,
ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive},
sync::Arc,
@ -197,8 +197,12 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> {
// TODO(joshie) TEMPORARY should be moved to trait providers
/// Traverse over changesets and plain state and recreate the [`PostState`]s for the given range
/// of blocks.
/// Unwind or peek at last N blocks of state recreating the [`BundleStateWithReceipts`].
///
/// If UNWIND it set to true tip and latest state will be unwind
/// and returned back with all the blocks
///
/// If UNWIND is false we will just read the state/blocks and return them.
///
/// 1. Iterate over the [BlockBodyIndices][tables::BlockBodyIndices] table to get all
/// the transaction ids.
@ -217,16 +221,14 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> {
/// 1. Take the old value from the changeset
/// 2. Take the new value from the local state
/// 3. Set the local state to the value in the changeset
///
/// If `TAKE` is `true`, the local state will be written to the plain state tables.
/// 5. Get all receipts from table
fn get_take_block_execution_result_range<const TAKE: bool>(
fn unwind_or_peek_state<const UNWIND: bool>(
&self,
range: RangeInclusive<BlockNumber>,
) -> Result<Vec<PostState>> {
) -> Result<BundleStateWithReceipts> {
if range.is_empty() {
return Ok(Vec::new())
return Ok(BundleStateWithReceipts::default())
}
let start_block_number = *range.start();
// We are not removing block meta as it is used to get block changesets.
let block_bodies = self.get_or_take::<tables::BlockBodyIndices, false>(range.clone())?;
@ -236,146 +238,139 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> {
block_bodies.first().expect("already checked if there are blocks").1.first_tx_num();
let to_transaction_num =
block_bodies.last().expect("already checked if there are blocks").1.last_tx_num();
let receipts =
self.get_or_take::<tables::Receipts, TAKE>(from_transaction_num..=to_transaction_num)?;
let storage_range = BlockNumberAddress::range(range.clone());
let storage_changeset =
self.get_or_take::<tables::StorageChangeSet, TAKE>(storage_range)?;
let account_changeset = self.get_or_take::<tables::AccountChangeSet, TAKE>(range)?;
self.get_or_take::<tables::StorageChangeSet, UNWIND>(storage_range)?;
let account_changeset = self.get_or_take::<tables::AccountChangeSet, UNWIND>(range)?;
// iterate previous value and get plain state value to create changeset
// Double option around Account represent if Account state is know (first option) and
// account is removed (Second Option)
type LocalPlainState = BTreeMap<Address, (Option<Option<Account>>, BTreeMap<H256, U256>)>;
let mut local_plain_state: LocalPlainState = BTreeMap::new();
// iterate in reverse and get plain state.
// Bundle execution changeset to its particular transaction and block
let mut block_states =
BTreeMap::from_iter(block_bodies.iter().map(|(num, _)| (*num, PostState::default())));
let mut state: BundleStateInit = HashMap::new();
// This is not working for blocks that are not at tip. as plain state is not the last
// state of end range. We should rename the functions or add support to access
// History state. Accessing history state can be tricky but we are not gaining
// anything.
let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
let mut plain_storage_cursor = self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
let mut reverts: RevertsInit = HashMap::new();
// add account changeset changes
for (block_number, account_before) in account_changeset.into_iter().rev() {
let AccountBeforeTx { info: old_info, address } = account_before;
let new_info = match local_plain_state.entry(address) {
Entry::Vacant(entry) => {
let new_account = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1);
entry.insert((Some(old_info), BTreeMap::new()));
new_account
match state.entry(address) {
hash_map::Entry::Vacant(entry) => {
let new_info = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1);
entry.insert((old_info, new_info, HashMap::new()));
}
Entry::Occupied(mut entry) => {
let new_account = std::mem::replace(&mut entry.get_mut().0, Some(old_info));
new_account.expect("As we are stacking account first, account would always be Some(Some) or Some(None)")
hash_map::Entry::Occupied(mut entry) => {
// overwrite old account state.
entry.get_mut().0 = old_info;
}
};
let post_state = block_states.entry(block_number).or_default();
match (old_info, new_info) {
(Some(old), Some(new)) => {
if new != old {
post_state.change_account(block_number, address, old, new);
} else {
unreachable!("Junk data in database: an account changeset did not represent any change");
}
}
(None, Some(account)) => post_state.create_account(block_number, address, account),
(Some(old), None) =>
post_state.destroy_account(block_number, address, old),
(None, None) => unreachable!("Junk data in database: an account changeset transitioned from no account to no account"),
};
}
// insert old info into reverts.
reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info);
}
// add storage changeset changes
let mut storage_changes: BTreeMap<BlockNumberAddress, StorageChangeset> = BTreeMap::new();
for (block_and_address, storage_entry) in storage_changeset.into_iter().rev() {
let BlockNumberAddress((_, address)) = block_and_address;
let new_storage =
match local_plain_state.entry(address).or_default().1.entry(storage_entry.key) {
Entry::Vacant(entry) => {
let new_storage = plain_storage_cursor
.seek_by_key_subkey(address, storage_entry.key)?
.filter(|storage| storage.key == storage_entry.key)
.unwrap_or_default();
entry.insert(storage_entry.value);
new_storage.value
}
Entry::Occupied(mut entry) => {
std::mem::replace(entry.get_mut(), storage_entry.value)
}
};
storage_changes.entry(block_and_address).or_default().insert(
U256::from_be_bytes(storage_entry.key.0),
(storage_entry.value, new_storage),
);
for (block_and_address, old_storage) in storage_changeset.into_iter().rev() {
let BlockNumberAddress((block_number, address)) = block_and_address;
// get account state or insert from plain state.
let account_state = match state.entry(address) {
hash_map::Entry::Vacant(entry) => {
let present_info = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1);
entry.insert((present_info, present_info, HashMap::new()))
}
hash_map::Entry::Occupied(entry) => entry.into_mut(),
};
// match storage.
match account_state.2.entry(old_storage.key) {
hash_map::Entry::Vacant(entry) => {
let new_storage = plain_storage_cursor
.seek_by_key_subkey(address, old_storage.key)?
.filter(|storage| storage.key == old_storage.key)
.unwrap_or_default();
entry.insert((old_storage.value, new_storage.value));
}
hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().0 = old_storage.value;
}
};
reverts
.entry(block_number)
.or_default()
.entry(address)
.or_default()
.1
.push(old_storage);
}
for (BlockNumberAddress((block_number, address)), storage_changeset) in
storage_changes.into_iter()
{
block_states.entry(block_number).or_default().change_storage(
block_number,
address,
storage_changeset,
);
}
if TAKE {
if UNWIND {
// iterate over local plain state remove all account and all storages.
for (address, (account, storage)) in local_plain_state.into_iter() {
// revert account
if let Some(account) = account {
let existing_entry = plain_accounts_cursor.seek_exact(address)?;
if let Some(account) = account {
plain_accounts_cursor.upsert(address, account)?;
for (address, (old_account, new_account, storage)) in state.iter() {
// revert account if needed.
if old_account != new_account {
let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
if let Some(account) = old_account {
plain_accounts_cursor.upsert(*address, *account)?;
} else if existing_entry.is_some() {
plain_accounts_cursor.delete_current()?;
}
}
// revert storages
for (storage_key, storage_value) in storage.into_iter() {
let storage_entry = StorageEntry { key: storage_key, value: storage_value };
for (storage_key, (old_storage_value, _new_storage_value)) in storage {
let storage_entry =
StorageEntry { key: *storage_key, value: *old_storage_value };
// delete previous value
// TODO: This does not use dupsort features
if plain_storage_cursor
.seek_by_key_subkey(address, storage_key)?
.filter(|s| s.key == storage_key)
.seek_by_key_subkey(*address, *storage_key)?
.filter(|s| s.key == *storage_key)
.is_some()
{
plain_storage_cursor.delete_current()?
}
// TODO: This does not use dupsort features
// insert value if needed
if storage_value != U256::ZERO {
plain_storage_cursor.upsert(address, storage_entry)?;
if *old_storage_value != U256::ZERO {
plain_storage_cursor.upsert(*address, storage_entry)?;
}
}
}
}
// iterate over block body and create ExecutionResult
let mut receipt_iter = receipts.into_iter();
let mut receipt_iter = self
.get_or_take::<tables::Receipts, UNWIND>(from_transaction_num..=to_transaction_num)?
.into_iter();
let mut receipts = Vec::new();
// loop break if we are at the end of the blocks.
for (block_number, block_body) in block_bodies.into_iter() {
for (_, block_body) in block_bodies.into_iter() {
let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize);
for _ in block_body.tx_num_range() {
if let Some((_, receipt)) = receipt_iter.next() {
block_states
.entry(block_number)
.or_default()
.add_receipt(block_number, receipt);
block_receipts.push(Some(receipt));
}
}
receipts.push(block_receipts);
}
Ok(block_states.into_values().collect())
Ok(BundleStateWithReceipts::new_init(
state,
reverts,
Vec::new(),
receipts,
start_block_number,
))
}
/// Return list of entries from table
@ -1826,11 +1821,12 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> HistoryWriter for DatabaseProvider
}
impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseProvider<'this, TX> {
/// Return range of blocks and its execution result
fn get_or_take_block_and_execution_range<const TAKE: bool>(
&self,
chain_spec: &ChainSpec,
range: RangeInclusive<BlockNumber>,
) -> Result<Vec<(SealedBlockWithSenders, PostState)>> {
) -> Result<Chain> {
if TAKE {
let storage_range = BlockNumberAddress::range(range.clone());
@ -1905,9 +1901,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseP
let blocks = self.get_take_block_range::<TAKE>(chain_spec, range.clone())?;
let unwind_to = blocks.first().map(|b| b.number.saturating_sub(1));
// get execution res
let execution_res = self.get_take_block_execution_result_range::<TAKE>(range.clone())?;
// combine them
let blocks_with_exec_result: Vec<_> = blocks.into_iter().zip(execution_res).collect();
let execution_state = self.unwind_or_peek_state::<TAKE>(range.clone())?;
// remove block bodies it is needed for both get block range and get block execution results
// that is why it is deleted afterwards.
@ -1921,8 +1915,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseP
}
}
// return them
Ok(blocks_with_exec_result)
Ok(Chain::new(blocks, execution_state))
}
}
@ -2021,10 +2014,10 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockWriter for DatabaseProvider<'
Ok(block_indices)
}
fn append_blocks_with_post_state(
fn append_blocks_with_bundle_state(
&self,
blocks: Vec<SealedBlockWithSenders>,
state: PostState,
state: BundleStateWithReceipts,
prune_modes: Option<&PruneModes>,
) -> Result<()> {
if blocks.is_empty() {
@ -2048,7 +2041,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockWriter for DatabaseProvider<'
// Write state and changesets to the database.
// Must be written after blocks because of the receipt lookup.
state.write_to_db(self.tx_ref(), new_tip_number)?;
state.write_to_db(self.tx_ref(), OriginalValuesKnown::No)?;
self.insert_hashes(first_number..=last_block_number, last_block_hash, expected_state_root)?;

View File

@ -1,8 +1,8 @@
use crate::{
BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications,
CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider,
PostStateDataProvider, ProviderError, PruneCheckpointReader, ReceiptProvider,
BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker,
CanonStateNotifications, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
EvmEnvProvider, HeaderProvider, ProviderError, PruneCheckpointReader, ReceiptProvider,
ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox, StateProviderFactory,
TransactionsProvider, WithdrawalsProvider,
};
@ -32,13 +32,13 @@ use std::{
};
use tracing::trace;
mod bundle_state_provider;
mod chain_info;
mod database;
mod post_state_provider;
mod state;
use crate::{providers::chain_info::ChainInfoTracker, traits::BlockSource};
pub use bundle_state_provider::BundleStateProvider;
pub use database::*;
pub use post_state_provider::PostStateProvider;
use reth_db::models::AccountBeforeTx;
use reth_interfaces::blockchain_tree::{
error::InsertBlockError, CanonicalOutcome, InsertPayloadOk,
@ -515,13 +515,13 @@ where
fn pending_with_provider(
&self,
post_state_data: Box<dyn PostStateDataProvider>,
post_state_data: Box<dyn BundleStateDataProvider>,
) -> Result<StateProviderBox<'_>> {
let canonical_fork = post_state_data.canonical_fork();
trace!(target: "providers::blockchain", ?canonical_fork, "Returning post state provider");
let state_provider = self.history_by_block_hash(canonical_fork.hash)?;
let post_state_provider = PostStateProvider::new(state_provider, post_state_data);
let post_state_provider = BundleStateProvider::new(state_provider, post_state_data);
Ok(Box::new(post_state_provider))
}
}
@ -754,7 +754,7 @@ where
fn find_pending_state_provider(
&self,
block_hash: BlockHash,
) -> Option<Box<dyn PostStateDataProvider>> {
) -> Option<Box<dyn BundleStateDataProvider>> {
self.tree.find_pending_state_provider(block_hash)
}
}

View File

@ -1,6 +1,6 @@
use crate::{
providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, PostState,
ProviderError, StateProvider, StateRootProvider,
providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader,
BundleStateWithReceipts, ProviderError, StateProvider, StateRootProvider,
};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
@ -201,7 +201,7 @@ impl<'a, 'b, TX: DbTx<'a>> BlockHashReader for HistoricalStateProviderRef<'a, 'b
}
impl<'a, 'b, TX: DbTx<'a>> StateRootProvider for HistoricalStateProviderRef<'a, 'b, TX> {
fn state_root(&self, _post_state: PostState) -> Result<H256> {
fn state_root(&self, _post_state: BundleStateWithReceipts) -> Result<H256> {
Err(ProviderError::StateRootNotAvailableForHistoricalBlock.into())
}
}

View File

@ -1,6 +1,6 @@
use crate::{
providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, PostState,
StateProvider, StateRootProvider,
providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader,
BundleStateWithReceipts, StateProvider, StateRootProvider,
};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
@ -56,8 +56,8 @@ impl<'a, 'b, TX: DbTx<'a>> BlockHashReader for LatestStateProviderRef<'a, 'b, TX
}
impl<'a, 'b, TX: DbTx<'a>> StateRootProvider for LatestStateProviderRef<'a, 'b, TX> {
fn state_root(&self, post_state: PostState) -> Result<H256> {
post_state
fn state_root(&self, bundle_state: BundleStateWithReceipts) -> Result<H256> {
bundle_state
.state_root_slow(self.db)
.map_err(|err| reth_interfaces::Error::Database(err.into()))
}

View File

@ -31,7 +31,7 @@ macro_rules! delegate_provider_impls {
$crate::providers::state::macros::delegate_impls_to_as_ref!(
for $target =>
StateRootProvider $(where [$($generics)*])? {
fn state_root(&self, state: crate::PostState) -> reth_interfaces::Result<reth_primitives::H256>;
fn state_root(&self, state: crate::BundleStateWithReceipts) -> reth_interfaces::Result<reth_primitives::H256>;
}
AccountReader $(where [$($generics)*])? {
fn basic_account(&self, address: reth_primitives::Address) -> reth_interfaces::Result<Option<reth_primitives::Account>>;

View File

@ -1,13 +1,13 @@
//! Dummy blocks and data for tests
use crate::{post_state::PostState, DatabaseProviderRW};
use crate::{BundleStateWithReceipts, DatabaseProviderRW};
use reth_db::{database::Database, models::StoredBlockBodyIndices, tables};
use reth_primitives::{
hex_literal::hex, Account, BlockNumber, Bytes, Header, Log, Receipt, SealedBlock,
SealedBlockWithSenders, TxType, Withdrawal, H160, H256, U256,
SealedBlockWithSenders, StorageEntry, TxType, Withdrawal, H160, H256, U256,
};
use reth_rlp::Decodable;
use std::collections::BTreeMap;
use std::collections::HashMap;
/// Assert genesis block
pub fn assert_genesis_block<DB: Database>(provider: &DatabaseProviderRW<'_, DB>, g: SealedBlock) {
@ -53,7 +53,7 @@ pub struct BlockChainTestData {
/// Genesis
pub genesis: SealedBlock,
/// Blocks with its execution result
pub blocks: Vec<(SealedBlockWithSenders, PostState)>,
pub blocks: Vec<(SealedBlockWithSenders, BundleStateWithReceipts)>,
}
impl BlockChainTestData {
@ -85,7 +85,7 @@ pub fn genesis() -> SealedBlock {
}
/// Block one that points to genesis
fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) {
fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceipts) {
let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
let mut block = SealedBlock::decode(&mut block_rlp).unwrap();
block.withdrawals = Some(vec![Withdrawal::default()]);
@ -96,27 +96,39 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) {
header.parent_hash = H256::zero();
block.header = header.seal_slow();
let mut post_state = PostState::default();
// Transaction changes
post_state.create_account(
number,
H160([0x61; 20]),
Account { nonce: 1, balance: U256::from(10), bytecode_hash: None },
);
post_state.create_account(
number,
H160([0x60; 20]),
Account { nonce: 1, balance: U256::from(10), bytecode_hash: None },
);
post_state.change_storage(
number,
H160([0x60; 20]),
BTreeMap::from([(U256::from(5), (U256::ZERO, U256::from(10)))]),
);
// block changes
let account1: H160 = [0x60; 20].into();
let account2: H160 = [0x61; 20].into();
let slot: H256 = H256::from_low_u64_be(5);
post_state.add_receipt(
number,
Receipt {
let bundle = BundleStateWithReceipts::new_init(
HashMap::from([
(
account1,
(
None,
Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
HashMap::from([(slot, (U256::from(0), U256::from(10)))]),
),
),
(
account2,
(
None,
Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
HashMap::from([]),
),
),
]),
HashMap::from([(
number,
HashMap::from([
(account1, (Some(None), vec![StorageEntry::new(slot, U256::from(0))])),
(account2, (Some(None), vec![])),
]),
)]),
vec![],
vec![vec![Some(Receipt {
tx_type: TxType::EIP2930,
success: true,
cumulative_gas_used: 300,
@ -125,14 +137,18 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) {
topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
data: Bytes::default(),
}],
},
})]],
number,
);
(SealedBlockWithSenders { block, senders: vec![H160([0x30; 20])] }, post_state)
(SealedBlockWithSenders { block, senders: vec![H160([0x30; 20])] }, bundle)
}
/// Block two that points to block 1
fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, PostState) {
fn block2(
number: BlockNumber,
parent_hash: H256,
) -> (SealedBlockWithSenders, BundleStateWithReceipts) {
let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
let mut block = SealedBlock::decode(&mut block_rlp).unwrap();
block.withdrawals = Some(vec![Withdrawal::default()]);
@ -144,22 +160,31 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po
header.parent_hash = parent_hash;
block.header = header.seal_slow();
let mut post_state = PostState::default();
// block changes
post_state.change_account(
number,
H160([0x60; 20]),
Account { nonce: 1, balance: U256::from(10), bytecode_hash: None },
Account { nonce: 3, balance: U256::from(20), bytecode_hash: None },
);
post_state.change_storage(
number,
H160([0x60; 20]),
BTreeMap::from([(U256::from(5), (U256::from(10), U256::from(15)))]),
);
post_state.add_receipt(
number,
Receipt {
let account: H160 = [0x60; 20].into();
let slot: H256 = H256::from_low_u64_be(5);
let bundle = BundleStateWithReceipts::new_init(
HashMap::from([(
account,
(
None,
Some(Account { nonce: 3, balance: U256::from(20), bytecode_hash: None }),
HashMap::from([(slot, (U256::from(0), U256::from(15)))]),
),
)]),
HashMap::from([(
number,
HashMap::from([(
account,
(
Some(Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None })),
vec![StorageEntry::new(slot, U256::from(10))],
),
)]),
)]),
vec![],
vec![vec![Some(Receipt {
tx_type: TxType::EIP1559,
success: false,
cumulative_gas_used: 400,
@ -168,8 +193,8 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po
topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)],
data: Bytes::default(),
}],
},
})]],
number,
);
(SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, post_state)
(SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, bundle)
}

View File

@ -1,19 +1,25 @@
use crate::{post_state::PostState, BlockExecutor, ExecutorFactory, StateProvider};
use crate::{
bundle_state::BundleStateWithReceipts, BlockExecutor, BlockExecutorStats, ExecutorFactory,
PrunableBlockExecutor, StateProvider,
};
use parking_lot::Mutex;
use reth_interfaces::executor::BlockExecutionError;
use reth_primitives::{Address, Block, ChainSpec, U256};
use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, U256};
use std::sync::Arc;
/// Test executor with mocked result.
pub struct TestExecutor(pub Option<PostState>);
pub struct TestExecutor(pub Option<BundleStateWithReceipts>);
impl<SP: StateProvider> BlockExecutor<SP> for TestExecutor {
impl BlockExecutor for TestExecutor {
fn execute(
&mut self,
_block: &Block,
_total_difficulty: U256,
_senders: Option<Vec<Address>>,
) -> Result<PostState, BlockExecutionError> {
self.0.clone().ok_or(BlockExecutionError::UnavailableForTest)
) -> Result<(), BlockExecutionError> {
if self.0.is_none() {
return Err(BlockExecutionError::UnavailableForTest)
}
Ok(())
}
fn execute_and_verify_receipt(
@ -21,15 +27,36 @@ impl<SP: StateProvider> BlockExecutor<SP> for TestExecutor {
_block: &Block,
_total_difficulty: U256,
_senders: Option<Vec<Address>>,
) -> Result<PostState, BlockExecutionError> {
self.0.clone().ok_or(BlockExecutionError::UnavailableForTest)
) -> Result<(), BlockExecutionError> {
if self.0.is_none() {
return Err(BlockExecutionError::UnavailableForTest)
}
Ok(())
}
fn take_output_state(&mut self) -> BundleStateWithReceipts {
self.0.clone().unwrap_or_default()
}
fn stats(&self) -> BlockExecutorStats {
BlockExecutorStats::default()
}
fn size_hint(&self) -> Option<usize> {
None
}
}
impl PrunableBlockExecutor for TestExecutor {
fn set_tip(&mut self, _tip: BlockNumber) {}
fn set_prune_modes(&mut self, _prune_modes: PruneModes) {}
}
/// Executor factory with pre-set execution results.
#[derive(Clone, Debug)]
pub struct TestExecutorFactory {
exec_results: Arc<Mutex<Vec<PostState>>>,
exec_results: Arc<Mutex<Vec<BundleStateWithReceipts>>>,
chain_spec: Arc<ChainSpec>,
}
@ -40,17 +67,18 @@ impl TestExecutorFactory {
}
/// Extend the mocked execution results
pub fn extend(&self, results: Vec<PostState>) {
pub fn extend(&self, results: Vec<BundleStateWithReceipts>) {
self.exec_results.lock().extend(results);
}
}
impl ExecutorFactory for TestExecutorFactory {
type Executor<T: StateProvider> = TestExecutor;
fn with_sp<SP: StateProvider>(&self, _sp: SP) -> Self::Executor<SP> {
fn with_state<'a, SP: StateProvider + 'a>(
&'a self,
_sp: SP,
) -> Box<dyn PrunableBlockExecutor + 'a> {
let exec_res = self.exec_results.lock().pop();
TestExecutor(exec_res)
Box::new(TestExecutor(exec_res))
}
fn chain_spec(&self) -> &ChainSpec {

View File

@ -1,7 +1,8 @@
use crate::{
bundle_state::BundleStateWithReceipts,
traits::{BlockSource, ReceiptProvider},
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
ChainSpecProvider, EvmEnvProvider, HeaderProvider, PostState, PostStateDataProvider,
BundleStateDataProvider, ChainSpecProvider, EvmEnvProvider, HeaderProvider,
ReceiptProviderIdExt, StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider,
TransactionsProvider, WithdrawalsProvider,
};
@ -399,7 +400,7 @@ impl AccountReader for MockEthProvider {
}
impl StateRootProvider for MockEthProvider {
fn state_root(&self, _post_state: PostState) -> Result<H256> {
fn state_root(&self, _state: BundleStateWithReceipts) -> Result<H256> {
todo!()
}
}
@ -498,7 +499,7 @@ impl StateProviderFactory for MockEthProvider {
fn pending_with_provider<'a>(
&'a self,
_post_state_data: Box<dyn PostStateDataProvider + 'a>,
_post_state_data: Box<dyn BundleStateDataProvider + 'a>,
) -> Result<StateProviderBox<'a>> {
todo!()
}
@ -531,7 +532,7 @@ impl StateProviderFactory for Arc<MockEthProvider> {
fn pending_with_provider<'a>(
&'a self,
_post_state_data: Box<dyn PostStateDataProvider + 'a>,
_post_state_data: Box<dyn BundleStateDataProvider + 'a>,
) -> Result<StateProviderBox<'a>> {
todo!()
}

View File

@ -1,10 +1,10 @@
use crate::{
bundle_state::BundleStateWithReceipts,
traits::{BlockSource, ReceiptProvider},
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PostState,
PruneCheckpointReader, ReceiptProviderIdExt, StageCheckpointReader, StateProvider,
StateProviderBox, StateProviderFactory, StateRootProvider, TransactionsProvider,
WithdrawalsProvider,
ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PruneCheckpointReader,
ReceiptProviderIdExt, StageCheckpointReader, StateProvider, StateProviderBox,
StateProviderFactory, StateRootProvider, TransactionsProvider, WithdrawalsProvider,
};
use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices};
use reth_interfaces::Result;
@ -243,7 +243,7 @@ impl ChangeSetReader for NoopProvider {
}
impl StateRootProvider for NoopProvider {
fn state_root(&self, _post_state: PostState) -> Result<H256> {
fn state_root(&self, _state: BundleStateWithReceipts) -> Result<H256> {
todo!()
}
}
@ -333,7 +333,7 @@ impl StateProviderFactory for NoopProvider {
fn pending_with_provider<'a>(
&'a self,
_post_state_data: Box<dyn crate::PostStateDataProvider + 'a>,
_post_state_data: Box<dyn crate::BundleStateDataProvider + 'a>,
) -> Result<StateProviderBox<'a>> {
Ok(Box::new(*self))
}

View File

@ -1,5 +1,5 @@
use crate::{
BlockIdReader, BlockNumReader, HeaderProvider, PostState, ReceiptProvider,
BlockIdReader, BlockNumReader, BundleStateWithReceipts, Chain, HeaderProvider, ReceiptProvider,
ReceiptProviderIdExt, TransactionsProvider, WithdrawalsProvider,
};
use auto_impl::auto_impl;
@ -210,7 +210,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync {
&self,
chain_spec: &ChainSpec,
range: RangeInclusive<BlockNumber>,
) -> Result<Vec<(SealedBlockWithSenders, PostState)>> {
) -> Result<Chain> {
self.get_or_take_block_and_execution_range::<false>(chain_spec, range)
}
@ -219,7 +219,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync {
&self,
chain_spec: &ChainSpec,
range: RangeInclusive<BlockNumber>,
) -> Result<Vec<(SealedBlockWithSenders, PostState)>> {
) -> Result<Chain> {
self.get_or_take_block_and_execution_range::<true>(chain_spec, range)
}
@ -228,7 +228,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync {
&self,
chain_spec: &ChainSpec,
range: RangeInclusive<BlockNumber>,
) -> Result<Vec<(SealedBlockWithSenders, PostState)>>;
) -> Result<Chain>;
}
/// Block Writer
@ -250,7 +250,7 @@ pub trait BlockWriter: Send + Sync {
/// updates the post-state.
///
/// Inserts the blocks into the database and updates the state with
/// provided `PostState`.
/// provided `BundleState`.
///
/// # Parameters
///
@ -261,11 +261,10 @@ pub trait BlockWriter: Send + Sync {
/// # Returns
///
/// Returns `Ok(())` on success, or an error if any operation fails.
fn append_blocks_with_post_state(
fn append_blocks_with_bundle_state(
&self,
blocks: Vec<SealedBlockWithSenders>,
state: PostState,
state: BundleStateWithReceipts,
prune_modes: Option<&PruneModes>,
) -> Result<()>;
}

View File

@ -59,8 +59,8 @@ impl Stream for CanonStateNotificationStream {
}
/// Chain action that is triggered when a new block is imported or old block is reverted.
/// and will return all [`crate::PostState`] and [`reth_primitives::SealedBlockWithSenders`] of both
/// reverted and committed blocks.
/// and will return all [`crate::BundleStateWithReceipts`] and
/// [`reth_primitives::SealedBlockWithSenders`] of both reverted and committed blocks.
#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub enum CanonStateNotification {

View File

@ -1,25 +1,27 @@
//! Executor Factory
use crate::{post_state::PostState, StateProvider};
use crate::{bundle_state::BundleStateWithReceipts, StateProvider};
use reth_interfaces::executor::BlockExecutionError;
use reth_primitives::{Address, Block, ChainSpec, U256};
use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, U256};
use std::time::Duration;
use tracing::info;
/// Executor factory that would create the EVM with particular state provider.
///
/// It can be used to mock executor.
pub trait ExecutorFactory: Send + Sync + 'static {
/// The executor produced by the factory
type Executor<T: StateProvider>: BlockExecutor<T>;
/// Executor with [`StateProvider`]
fn with_sp<SP: StateProvider>(&self, sp: SP) -> Self::Executor<SP>;
fn with_state<'a, SP: StateProvider + 'a>(
&'a self,
_sp: SP,
) -> Box<dyn PrunableBlockExecutor + 'a>;
/// Return internal chainspec
fn chain_spec(&self) -> &ChainSpec;
}
/// An executor capable of executing a block.
pub trait BlockExecutor<SP: StateProvider> {
pub trait BlockExecutor {
/// Execute a block.
///
/// The number of `senders` should be equal to the number of transactions in the block.
@ -33,13 +35,63 @@ pub trait BlockExecutor<SP: StateProvider> {
block: &Block,
total_difficulty: U256,
senders: Option<Vec<Address>>,
) -> Result<PostState, BlockExecutionError>;
) -> Result<(), BlockExecutionError>;
/// Executes the block and checks receipts
/// Executes the block and checks receipts.
fn execute_and_verify_receipt(
&mut self,
block: &Block,
total_difficulty: U256,
senders: Option<Vec<Address>>,
) -> Result<PostState, BlockExecutionError>;
) -> Result<(), BlockExecutionError>;
/// Return bundle state. This is output of executed blocks.
fn take_output_state(&mut self) -> BundleStateWithReceipts;
/// Internal statistics of execution.
fn stats(&self) -> BlockExecutorStats;
/// Returns the size hint of current in-memory changes.
fn size_hint(&self) -> Option<usize>;
}
/// A [BlockExecutor] capable of in-memory pruning of the data that will be written to the database.
pub trait PrunableBlockExecutor: BlockExecutor {
/// Set tip - highest known block number.
fn set_tip(&mut self, tip: BlockNumber);
/// Set prune modes.
fn set_prune_modes(&mut self, prune_modes: PruneModes);
}
/// Block execution statistics. Contains duration of each step of block execution.
#[derive(Clone, Debug, Default)]
pub struct BlockExecutorStats {
/// Execution duration.
pub execution_duration: Duration,
/// Time needed to apply output of revm execution to revm cached state.
pub apply_state_duration: Duration,
/// Time needed to apply post execution state changes.
pub apply_post_execution_state_changes_duration: Duration,
/// Time needed to merge transitions and create reverts.
/// It this time transitions are applies to revm bundle state.
pub merge_transitions_duration: Duration,
/// Time needed to caclulate receipt roots.
pub receipt_root_duration: Duration,
/// Time needed to recovere senders.
pub sender_recovery_duration: Duration,
}
impl BlockExecutorStats {
/// Log duration to info level log.
pub fn log_info(&self) {
info!(target: "evm",
evm_transact = ?self.execution_duration,
apply_state = ?self.apply_state_duration,
apply_post_state = ?self.apply_post_execution_state_changes_duration,
merge_transitions = ?self.merge_transitions_duration,
receipt_root = ?self.receipt_root_duration,
sender_recovery = ?self.sender_recovery_duration,
"Execution time");
}
}

View File

@ -29,7 +29,7 @@ pub use receipts::{ReceiptProvider, ReceiptProviderIdExt};
mod state;
pub use state::{
BlockchainTreePendingStateProvider, PostStateDataProvider, StateProvider, StateProviderBox,
BlockchainTreePendingStateProvider, BundleStateDataProvider, StateProvider, StateProviderBox,
StateProviderFactory, StateRootProvider,
};
@ -40,7 +40,7 @@ mod withdrawals;
pub use withdrawals::WithdrawalsProvider;
mod executor;
pub use executor::{BlockExecutor, ExecutorFactory};
pub use executor::{BlockExecutor, BlockExecutorStats, ExecutorFactory, PrunableBlockExecutor};
mod chain;
pub use chain::{

View File

@ -1,5 +1,5 @@
use super::AccountReader;
use crate::{post_state::PostState, BlockHashReader, BlockIdReader};
use crate::{BlockHashReader, BlockIdReader, BundleStateWithReceipts};
use auto_impl::auto_impl;
use reth_interfaces::{provider::ProviderError, Result};
use reth_primitives::{
@ -177,7 +177,7 @@ pub trait StateProviderFactory: BlockIdReader + Send + Sync {
/// Used to inspect or execute transaction on the pending state.
fn pending_with_provider(
&self,
post_state_data: Box<dyn PostStateDataProvider>,
post_state_data: Box<dyn BundleStateDataProvider>,
) -> Result<StateProviderBox<'_>>;
}
@ -191,7 +191,7 @@ pub trait BlockchainTreePendingStateProvider: Send + Sync {
fn pending_state_provider(
&self,
block_hash: BlockHash,
) -> Result<Box<dyn PostStateDataProvider>> {
) -> Result<Box<dyn BundleStateDataProvider>> {
Ok(self
.find_pending_state_provider(block_hash)
.ok_or(ProviderError::StateForHashNotFound(block_hash))?)
@ -201,20 +201,20 @@ pub trait BlockchainTreePendingStateProvider: Send + Sync {
fn find_pending_state_provider(
&self,
block_hash: BlockHash,
) -> Option<Box<dyn PostStateDataProvider>>;
) -> Option<Box<dyn BundleStateDataProvider>>;
}
/// Post state data needs for execution on it.
/// This trait is used to create a state provider over pending state.
///
/// Pending state contains:
/// * [`PostState`] contains all changed of accounts and storage of pending chain
/// * [`BundleStateWithReceipts`] contains all changed of accounts and storage of pending chain
/// * block hashes of pending chain and canonical blocks.
/// * canonical fork, the block on what pending chain was forked from.
#[auto_impl[Box,&]]
pub trait PostStateDataProvider: Send + Sync {
pub trait BundleStateDataProvider: Send + Sync {
/// Return post state
fn state(&self) -> &PostState;
fn state(&self) -> &BundleStateWithReceipts;
/// Return block hash by block number of pending or canonical chain.
fn block_hash(&self, block_number: BlockNumber) -> Option<BlockHash>;
/// return canonical fork, the block on what post state was forked from.
@ -226,7 +226,6 @@ pub trait PostStateDataProvider: Send + Sync {
/// A type that can compute the state root of a given post state.
#[auto_impl[Box,&, Arc]]
pub trait StateRootProvider: Send + Sync {
/// Returns the state root of the PostState on top of the current state.
/// See [PostState::state_root_slow] for more info.
fn state_root(&self, post_state: PostState) -> Result<H256>;
/// Returns the state root of the BundleState on top of the current state.
fn state_root(&self, post_state: BundleStateWithReceipts) -> Result<H256>;
}

View File

@ -1,197 +0,0 @@
use reth_interfaces::{db::DatabaseError as DbError, provider::ProviderError};
use reth_primitives::{BlockHash, BlockNumber, H256};
use reth_trie::StateRootError;
use std::fmt::Debug;
#[cfg(test)]
mod test {
use crate::{test_utils::blocks::*, ProviderFactory, TransactionsProvider};
use reth_db::{
models::{storage_sharded_key::StorageShardedKey, ShardedKey},
tables,
test_utils::create_test_rw_db,
};
use reth_primitives::{ChainSpecBuilder, IntegerList, H160, MAINNET, U256};
use std::sync::Arc;
#[test]
fn insert_block_and_hashes_get_take() {
let db = create_test_rw_db();
// setup
let chain_spec = ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(MAINNET.genesis.clone())
.shanghai_activated()
.build();
let factory = ProviderFactory::new(db.as_ref(), Arc::new(chain_spec.clone()));
let provider = factory.provider_rw().unwrap();
let data = BlockChainTestData::default();
let genesis = data.genesis.clone();
let (block1, exec_res1) = data.blocks[0].clone();
let (block2, exec_res2) = data.blocks[1].clone();
let acc1_shard_key = ShardedKey::new(H160([0x60; 20]), u64::MAX);
let acc2_shard_key = ShardedKey::new(H160([0x61; 20]), u64::MAX);
let storage1_shard_key =
StorageShardedKey::new(H160([0x60; 20]), U256::from(5).into(), u64::MAX);
provider.insert_block(data.genesis.clone(), None).unwrap();
assert_genesis_block(&provider, data.genesis);
provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap();
assert_eq!(
provider.table::<tables::AccountHistory>().unwrap(),
vec![
(acc1_shard_key.clone(), IntegerList::new(vec![1]).unwrap()),
(acc2_shard_key.clone(), IntegerList::new(vec![1]).unwrap())
]
);
assert_eq!(
provider.table::<tables::StorageHistory>().unwrap(),
vec![(storage1_shard_key.clone(), IntegerList::new(vec![1]).unwrap())]
);
// get one block
let get = provider.get_block_and_execution_range(&chain_spec, 1..=1).unwrap();
let get_block = get[0].0.clone();
let get_state = get[0].1.clone();
assert_eq!(get_block, block1);
assert_eq!(get_state, exec_res1);
// take one block
let take = provider.take_block_and_execution_range(&chain_spec, 1..=1).unwrap();
assert_eq!(take, vec![(block1.clone(), exec_res1.clone())]);
assert_genesis_block(&provider, genesis.clone());
// check if history is empty.
assert_eq!(provider.table::<tables::AccountHistory>().unwrap(), vec![]);
assert_eq!(provider.table::<tables::StorageHistory>().unwrap(), vec![]);
provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap();
provider.append_blocks_with_post_state(vec![block2.clone()], exec_res2.clone()).unwrap();
// check history of two blocks
assert_eq!(
provider.table::<tables::AccountHistory>().unwrap(),
vec![
(acc1_shard_key, IntegerList::new(vec![1, 2]).unwrap()),
(acc2_shard_key, IntegerList::new(vec![1]).unwrap())
]
);
assert_eq!(
provider.table::<tables::StorageHistory>().unwrap(),
vec![(storage1_shard_key, IntegerList::new(vec![1, 2]).unwrap())]
);
provider.commit().unwrap();
// Check that transactions map onto blocks correctly.
{
let provider = factory.provider_rw().unwrap();
assert_eq!(
provider.transaction_block(0).unwrap(),
Some(1),
"Transaction 0 should be in block 1"
);
assert_eq!(
provider.transaction_block(1).unwrap(),
Some(2),
"Transaction 1 should be in block 2"
);
assert_eq!(
provider.transaction_block(2).unwrap(),
None,
"Transaction 0 should not exist"
);
}
let provider = factory.provider_rw().unwrap();
// get second block
let get = provider.get_block_and_execution_range(&chain_spec, 2..=2).unwrap();
assert_eq!(get, vec![(block2.clone(), exec_res2.clone())]);
// get two blocks
let get = provider.get_block_and_execution_range(&chain_spec, 1..=2).unwrap();
assert_eq!(get[0].0, block1);
assert_eq!(get[1].0, block2);
assert_eq!(get[0].1, exec_res1);
assert_eq!(get[1].1, exec_res2);
// take two blocks
let get = provider.take_block_and_execution_range(&chain_spec, 1..=2).unwrap();
assert_eq!(get, vec![(block1, exec_res1), (block2, exec_res2)]);
// assert genesis state
assert_genesis_block(&provider, genesis);
}
#[test]
fn insert_get_take_multiblocks() {
let db = create_test_rw_db();
// setup
let chain_spec = Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(MAINNET.genesis.clone())
.shanghai_activated()
.build(),
);
let factory = ProviderFactory::new(db.as_ref(), chain_spec.clone());
let provider = factory.provider_rw().unwrap();
let data = BlockChainTestData::default();
let genesis = data.genesis.clone();
let (block1, exec_res1) = data.blocks[0].clone();
let (block2, exec_res2) = data.blocks[1].clone();
provider.insert_block(data.genesis.clone(), None).unwrap();
assert_genesis_block(&provider, data.genesis);
provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap();
// get one block
let get = provider.get_block_and_execution_range(&chain_spec, 1..=1).unwrap();
assert_eq!(get, vec![(block1.clone(), exec_res1.clone())]);
// take one block
let take = provider.take_block_and_execution_range(&chain_spec, 1..=1).unwrap();
assert_eq!(take, vec![(block1.clone(), exec_res1.clone())]);
assert_genesis_block(&provider, genesis.clone());
// insert two blocks
let mut merged_state = exec_res1.clone();
merged_state.extend(exec_res2.clone());
provider
.append_blocks_with_post_state(
vec![block1.clone(), block2.clone()],
merged_state.clone(),
)
.unwrap();
// get second block
let get = provider.get_block_and_execution_range(&chain_spec, 2..=2).unwrap();
assert_eq!(get, vec![(block2.clone(), exec_res2.clone())]);
// get two blocks
let get = provider.get_block_and_execution_range(&chain_spec, 1..=2).unwrap();
assert_eq!(
get,
vec![(block1.clone(), exec_res1.clone()), (block2.clone(), exec_res2.clone())]
);
// take two blocks
let get = provider.take_block_and_execution_range(&chain_spec, 1..=2).unwrap();
assert_eq!(get, vec![(block1, exec_res1), (block2, exec_res2)]);
// assert genesis state
assert_genesis_block(&provider, genesis);
}
}