mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
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:
@ -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
11
crates/storage/provider/src/bundle_state/mod.rs
Normal file
11
crates/storage/provider/src/bundle_state/mod.rs
Normal 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;
|
||||
88
crates/storage/provider/src/bundle_state/state_changes.rs
Normal file
88
crates/storage/provider/src/bundle_state/state_changes.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
167
crates/storage/provider/src/bundle_state/state_reverts.rs
Normal file
167
crates/storage/provider/src/bundle_state/state_reverts.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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())]) };
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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
@ -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
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)?;
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()))
|
||||
}
|
||||
|
||||
@ -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>>;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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!()
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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<()>;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::{
|
||||
|
||||
@ -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>;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user