mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
refactor: use BlockExecutionOutcome in ExecutedBlock (#21123)
This commit is contained in:
@@ -10,7 +10,7 @@ use alloy_primitives::{map::HashMap, BlockNumber, TxHash, B256};
|
||||
use parking_lot::RwLock;
|
||||
use reth_chainspec::ChainInfo;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_execution_types::{Chain, ExecutionOutcome};
|
||||
use reth_execution_types::{BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome};
|
||||
use reth_metrics::{metrics::Gauge, Metrics};
|
||||
use reth_primitives_traits::{
|
||||
BlockBody as _, IndexedTx, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader,
|
||||
@@ -18,7 +18,7 @@ use reth_primitives_traits::{
|
||||
};
|
||||
use reth_storage_api::StateProviderBox;
|
||||
use reth_trie::{updates::TrieUpdatesSorted, HashedPostStateSorted, TrieInputSorted};
|
||||
use std::{collections::BTreeMap, ops::Deref, sync::Arc, time::Instant};
|
||||
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
||||
use tokio::sync::{broadcast, watch};
|
||||
|
||||
/// Size of the broadcast channel used to notify canonical state events.
|
||||
@@ -648,7 +648,7 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
}
|
||||
|
||||
/// Returns the `Receipts` of executed block that determines the state.
|
||||
pub fn receipts(&self) -> &Vec<Vec<N::Receipt>> {
|
||||
pub fn receipts(&self) -> &Vec<N::Receipt> {
|
||||
&self.block.execution_outcome().receipts
|
||||
}
|
||||
|
||||
@@ -659,15 +659,7 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
///
|
||||
/// This clones the vector of receipts. To avoid it, use [`Self::executed_block_receipts_ref`].
|
||||
pub fn executed_block_receipts(&self) -> Vec<N::Receipt> {
|
||||
let receipts = self.receipts();
|
||||
|
||||
debug_assert!(
|
||||
receipts.len() <= 1,
|
||||
"Expected at most one block's worth of receipts, found {}",
|
||||
receipts.len()
|
||||
);
|
||||
|
||||
receipts.first().cloned().unwrap_or_default()
|
||||
self.receipts().clone()
|
||||
}
|
||||
|
||||
/// Returns a slice of `Receipt` of executed block that determines the state.
|
||||
@@ -675,15 +667,7 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
/// has only one element corresponding to the executed block associated to
|
||||
/// the state.
|
||||
pub fn executed_block_receipts_ref(&self) -> &[N::Receipt] {
|
||||
let receipts = self.receipts();
|
||||
|
||||
debug_assert!(
|
||||
receipts.len() <= 1,
|
||||
"Expected at most one block's worth of receipts, found {}",
|
||||
receipts.len()
|
||||
);
|
||||
|
||||
receipts.first().map(|receipts| receipts.deref()).unwrap_or_default()
|
||||
self.receipts()
|
||||
}
|
||||
|
||||
/// Returns an iterator over __parent__ `BlockStates`.
|
||||
@@ -767,7 +751,7 @@ pub struct ExecutedBlock<N: NodePrimitives = EthPrimitives> {
|
||||
/// Recovered Block
|
||||
pub recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
/// Block's execution outcome.
|
||||
pub execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
pub execution_output: Arc<BlockExecutionOutput<N::Receipt>>,
|
||||
/// Deferred trie data produced by execution.
|
||||
///
|
||||
/// This allows deferring the computation of the trie data which can be expensive.
|
||||
@@ -779,7 +763,15 @@ impl<N: NodePrimitives> Default for ExecutedBlock<N> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
recovered_block: Default::default(),
|
||||
execution_output: Default::default(),
|
||||
execution_output: Arc::new(BlockExecutionOutput {
|
||||
result: BlockExecutionResult {
|
||||
receipts: Default::default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
state: Default::default(),
|
||||
}),
|
||||
trie_data: DeferredTrieData::ready(ComputedTrieData::default()),
|
||||
}
|
||||
}
|
||||
@@ -800,7 +792,7 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
/// payload builders). This is the safe default path.
|
||||
pub fn new(
|
||||
recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
execution_output: Arc<BlockExecutionOutput<N::Receipt>>,
|
||||
trie_data: ComputedTrieData,
|
||||
) -> Self {
|
||||
Self { recovered_block, execution_output, trie_data: DeferredTrieData::ready(trie_data) }
|
||||
@@ -822,7 +814,7 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
/// Use [`Self::new()`] instead when trie data is already computed and available immediately.
|
||||
pub const fn with_deferred_trie_data(
|
||||
recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
execution_output: Arc<BlockExecutionOutput<N::Receipt>>,
|
||||
trie_data: DeferredTrieData,
|
||||
) -> Self {
|
||||
Self { recovered_block, execution_output, trie_data }
|
||||
@@ -842,7 +834,7 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
|
||||
/// Returns a reference to the block's execution outcome
|
||||
#[inline]
|
||||
pub fn execution_outcome(&self) -> &ExecutionOutcome<N::Receipt> {
|
||||
pub fn execution_outcome(&self) -> &BlockExecutionOutput<N::Receipt> {
|
||||
&self.execution_output
|
||||
}
|
||||
|
||||
@@ -958,14 +950,20 @@ impl<N: NodePrimitives<SignedTx: SignedTransaction>> NewCanonicalChain<N> {
|
||||
[first, rest @ ..] => {
|
||||
let mut chain = Chain::from_block(
|
||||
first.recovered_block().clone(),
|
||||
first.execution_outcome().clone(),
|
||||
ExecutionOutcome::from((
|
||||
first.execution_outcome().clone(),
|
||||
first.block_number(),
|
||||
)),
|
||||
first.trie_updates(),
|
||||
first.hashed_state(),
|
||||
);
|
||||
for exec in rest {
|
||||
chain.append_block(
|
||||
exec.recovered_block().clone(),
|
||||
exec.execution_outcome().clone(),
|
||||
ExecutionOutcome::from((
|
||||
exec.execution_outcome().clone(),
|
||||
exec.block_number(),
|
||||
)),
|
||||
exec.trie_updates(),
|
||||
exec.hashed_state(),
|
||||
);
|
||||
@@ -1264,7 +1262,7 @@ mod tests {
|
||||
|
||||
let state = BlockState::new(block);
|
||||
|
||||
assert_eq!(state.receipts(), &receipts);
|
||||
assert_eq!(state.receipts(), receipts.first().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -3,10 +3,7 @@ use crate::{
|
||||
CanonStateSubscriptions, ComputedTrieData,
|
||||
};
|
||||
use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH};
|
||||
use alloy_eips::{
|
||||
eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, INITIAL_BASE_FEE},
|
||||
eip7685::Requests,
|
||||
};
|
||||
use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, INITIAL_BASE_FEE};
|
||||
use alloy_primitives::{Address, BlockNumber, B256, U256};
|
||||
use alloy_signer::SignerSync;
|
||||
use alloy_signer_local::PrivateKeySigner;
|
||||
@@ -16,7 +13,7 @@ use reth_chainspec::{ChainSpec, EthereumHardfork, MIN_TRANSACTION_GAS};
|
||||
use reth_ethereum_primitives::{
|
||||
Block, BlockBody, EthPrimitives, Receipt, Transaction, TransactionSigned,
|
||||
};
|
||||
use reth_execution_types::{Chain, ExecutionOutcome};
|
||||
use reth_execution_types::{BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome};
|
||||
use reth_primitives_traits::{
|
||||
proofs::{calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root},
|
||||
Account, NodePrimitives, Recovered, RecoveredBlock, SealedBlock, SealedHeader,
|
||||
@@ -201,7 +198,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
fn get_executed_block(
|
||||
&mut self,
|
||||
block_number: BlockNumber,
|
||||
receipts: Vec<Vec<Receipt>>,
|
||||
mut receipts: Vec<Vec<Receipt>>,
|
||||
parent_hash: B256,
|
||||
) -> ExecutedBlock {
|
||||
let block = self.generate_random_block(block_number, parent_hash);
|
||||
@@ -209,12 +206,15 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
let trie_data = ComputedTrieData::default();
|
||||
ExecutedBlock::new(
|
||||
Arc::new(RecoveredBlock::new_sealed(block, senders)),
|
||||
Arc::new(ExecutionOutcome::new(
|
||||
BundleState::default(),
|
||||
receipts,
|
||||
block_number,
|
||||
vec![Requests::default()],
|
||||
)),
|
||||
Arc::new(BlockExecutionOutput {
|
||||
result: BlockExecutionResult {
|
||||
receipts: receipts.pop().unwrap_or_default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
state: BundleState::default(),
|
||||
}),
|
||||
trie_data,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ use reth_payload_primitives::{
|
||||
};
|
||||
use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader};
|
||||
use reth_provider::{
|
||||
BlockNumReader, BlockReader, ChangeSetReader, DatabaseProviderFactory, HashedPostStateProvider,
|
||||
ProviderError, StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader,
|
||||
TransactionVariant,
|
||||
BlockExecutionOutput, BlockExecutionResult, BlockNumReader, BlockReader, ChangeSetReader,
|
||||
DatabaseProviderFactory, HashedPostStateProvider, ProviderError, StageCheckpointReader,
|
||||
StateProviderBox, StateProviderFactory, StateReader, TransactionVariant,
|
||||
};
|
||||
use reth_revm::database::StateProviderDatabase;
|
||||
use reth_stages_api::ControlFlow;
|
||||
@@ -1856,7 +1856,7 @@ where
|
||||
.sealed_block_with_senders(hash.into(), TransactionVariant::WithHash)?
|
||||
.ok_or_else(|| ProviderError::HeaderNotFound(hash.into()))?
|
||||
.split_sealed();
|
||||
let execution_output = self
|
||||
let mut execution_output = self
|
||||
.provider
|
||||
.get_state(block.header().number())?
|
||||
.ok_or_else(|| ProviderError::StateForNumberNotFound(block.header().number()))?;
|
||||
@@ -1880,9 +1880,19 @@ where
|
||||
let trie_data =
|
||||
ComputedTrieData::without_trie_input(sorted_hashed_state, sorted_trie_updates);
|
||||
|
||||
let execution_output = Arc::new(BlockExecutionOutput {
|
||||
state: execution_output.bundle,
|
||||
result: BlockExecutionResult {
|
||||
receipts: execution_output.receipts.pop().unwrap_or_default(),
|
||||
requests: execution_output.requests.pop().unwrap_or_default(),
|
||||
gas_used: block.gas_used(),
|
||||
blob_gas_used: block.blob_gas_used().unwrap_or_default(),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(Some(ExecutedBlock::new(
|
||||
Arc::new(RecoveredBlock::new_sealed(block, senders)),
|
||||
Arc::new(execution_output),
|
||||
execution_output,
|
||||
trie_data,
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ use reth_evm::{
|
||||
ConfigureEvm, EvmEnvFor, ExecutableTxIterator, ExecutableTxTuple, OnStateHook, SpecFor,
|
||||
TxEnvFor,
|
||||
};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_primitives_traits::NodePrimitives;
|
||||
use reth_provider::{
|
||||
BlockReader, DatabaseProviderROFactory, StateProvider, StateProviderFactory, StateReader,
|
||||
BlockExecutionOutput, BlockReader, DatabaseProviderROFactory, StateProvider,
|
||||
StateProviderFactory, StateReader,
|
||||
};
|
||||
use reth_revm::{db::BundleState, state::EvmState};
|
||||
use reth_trie::{hashed_cursor::HashedCursorFactory, trie_cursor::TrieCursorFactory};
|
||||
@@ -665,12 +665,12 @@ impl<Tx, Err, R: Send + Sync + 'static> PayloadHandle<Tx, Err, R> {
|
||||
|
||||
/// Terminates the entire caching task.
|
||||
///
|
||||
/// If the [`ExecutionOutcome`] is provided it will update the shared cache using its
|
||||
/// If the [`BlockExecutionOutput`] is provided it will update the shared cache using its
|
||||
/// bundle state. Using `Arc<ExecutionOutcome>` allows sharing with the main execution
|
||||
/// path without cloning the expensive `BundleState`.
|
||||
pub(super) fn terminate_caching(
|
||||
&mut self,
|
||||
execution_outcome: Option<Arc<ExecutionOutcome<R>>>,
|
||||
execution_outcome: Option<Arc<BlockExecutionOutput<R>>>,
|
||||
) {
|
||||
self.prewarm_handle.terminate_caching(execution_outcome)
|
||||
}
|
||||
@@ -707,11 +707,11 @@ impl<R: Send + Sync + 'static> CacheTaskHandle<R> {
|
||||
|
||||
/// Terminates the entire pre-warming task.
|
||||
///
|
||||
/// If the [`ExecutionOutcome`] is provided it will update the shared cache using its
|
||||
/// If the [`BlockExecutionOutput`] is provided it will update the shared cache using its
|
||||
/// bundle state. Using `Arc<ExecutionOutcome>` avoids cloning the expensive `BundleState`.
|
||||
pub(super) fn terminate_caching(
|
||||
&mut self,
|
||||
execution_outcome: Option<Arc<ExecutionOutcome<R>>>,
|
||||
execution_outcome: Option<Arc<BlockExecutionOutput<R>>>,
|
||||
) {
|
||||
if let Some(tx) = self.to_prewarm_task.take() {
|
||||
let event = PrewarmTaskEvent::Terminate { execution_outcome };
|
||||
|
||||
@@ -30,10 +30,12 @@ use alloy_primitives::{keccak256, map::B256Set, B256};
|
||||
use crossbeam_channel::Sender as CrossbeamSender;
|
||||
use metrics::{Counter, Gauge, Histogram};
|
||||
use reth_evm::{execute::ExecutableTxFor, ConfigureEvm, Evm, EvmFor, SpecFor};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_metrics::Metrics;
|
||||
use reth_primitives_traits::NodePrimitives;
|
||||
use reth_provider::{AccountReader, BlockReader, StateProvider, StateProviderFactory, StateReader};
|
||||
use reth_provider::{
|
||||
AccountReader, BlockExecutionOutput, BlockReader, StateProvider, StateProviderFactory,
|
||||
StateReader,
|
||||
};
|
||||
use reth_revm::{database::StateProviderDatabase, state::EvmState};
|
||||
use reth_trie::MultiProofTargets;
|
||||
use std::{
|
||||
@@ -259,7 +261,7 @@ where
|
||||
///
|
||||
/// This method is called from `run()` only after all execution tasks are complete.
|
||||
#[instrument(level = "debug", target = "engine::tree::payload_processor::prewarm", skip_all)]
|
||||
fn save_cache(self, execution_outcome: Arc<ExecutionOutcome<N::Receipt>>) {
|
||||
fn save_cache(self, execution_outcome: Arc<BlockExecutionOutput<N::Receipt>>) {
|
||||
let start = Instant::now();
|
||||
|
||||
let Self { execution_cache, ctx: PrewarmContext { env, metrics, saved_cache, .. }, .. } =
|
||||
@@ -277,7 +279,7 @@ where
|
||||
|
||||
// Insert state into cache while holding the lock
|
||||
// Access the BundleState through the shared ExecutionOutcome
|
||||
if new_cache.cache().insert_state(execution_outcome.state()).is_err() {
|
||||
if new_cache.cache().insert_state(&execution_outcome.state).is_err() {
|
||||
// Clear the cache on error to prevent having a polluted cache
|
||||
*cached = None;
|
||||
debug!(target: "engine::caching", "cleared execution cache on update error");
|
||||
@@ -810,7 +812,7 @@ pub(super) enum PrewarmTaskEvent<R> {
|
||||
Terminate {
|
||||
/// The final execution outcome. Using `Arc` allows sharing with the main execution
|
||||
/// path without cloning the expensive `BundleState`.
|
||||
execution_outcome: Option<Arc<ExecutionOutcome<R>>>,
|
||||
execution_outcome: Option<Arc<BlockExecutionOutput<R>>>,
|
||||
},
|
||||
/// The outcome of a pre-warm task
|
||||
Outcome {
|
||||
|
||||
@@ -41,9 +41,9 @@ use reth_primitives_traits::{
|
||||
};
|
||||
use reth_provider::{
|
||||
providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockNumReader, BlockReader,
|
||||
ChangeSetReader, DatabaseProviderFactory, DatabaseProviderROFactory, ExecutionOutcome,
|
||||
HashedPostStateProvider, ProviderError, PruneCheckpointReader, StageCheckpointReader,
|
||||
StateProvider, StateProviderFactory, StateReader,
|
||||
ChangeSetReader, DatabaseProviderFactory, DatabaseProviderROFactory, HashedPostStateProvider,
|
||||
ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProvider,
|
||||
StateProviderFactory, StateReader,
|
||||
};
|
||||
use reth_revm::db::State;
|
||||
use reth_trie::{
|
||||
@@ -376,7 +376,6 @@ where
|
||||
}
|
||||
|
||||
let parent_hash = input.parent_hash();
|
||||
let block_num_hash = input.num_hash();
|
||||
|
||||
trace!(target: "engine::tree::payload_validator", "Fetching block state provider");
|
||||
let _enter =
|
||||
@@ -586,7 +585,7 @@ where
|
||||
|
||||
// Create ExecutionOutcome and wrap in Arc for sharing with both the caching task
|
||||
// and the deferred trie task. This avoids cloning the expensive BundleState.
|
||||
let execution_outcome = Arc::new(ExecutionOutcome::from((output, block_num_hash.number)));
|
||||
let execution_outcome = Arc::new(output);
|
||||
|
||||
// Terminate prewarming task with the shared execution outcome
|
||||
handle.terminate_caching(Some(Arc::clone(&execution_outcome)));
|
||||
@@ -1097,7 +1096,7 @@ where
|
||||
fn spawn_deferred_trie_task(
|
||||
&self,
|
||||
block: RecoveredBlock<N::Block>,
|
||||
execution_outcome: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
execution_outcome: Arc<BlockExecutionOutput<N::Receipt>>,
|
||||
ctx: &TreeCtx<'_, N>,
|
||||
hashed_state: HashedPostState,
|
||||
trie_output: TrieUpdates,
|
||||
@@ -1344,7 +1343,7 @@ where
|
||||
fn on_inserted_executed_block(&self, block: ExecutedBlock<N>) {
|
||||
self.payload_processor.on_inserted_executed_block(
|
||||
block.recovered_block.block_with_parent(),
|
||||
block.execution_output.state(),
|
||||
&block.execution_output.state,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use reth_ethereum_engine_primitives::EthEngineTypes;
|
||||
use reth_ethereum_primitives::{Block, EthPrimitives};
|
||||
use reth_evm_ethereum::MockEvmConfig;
|
||||
use reth_primitives_traits::Block as _;
|
||||
use reth_provider::{test_utils::MockEthProvider, ExecutionOutcome};
|
||||
use reth_provider::test_utils::MockEthProvider;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
str::FromStr,
|
||||
@@ -838,7 +838,7 @@ fn test_tree_state_on_new_head_deep_fork() {
|
||||
for block in &chain_a {
|
||||
test_harness.tree.state.tree_state.insert_executed(ExecutedBlock::new(
|
||||
Arc::new(block.clone()),
|
||||
Arc::new(ExecutionOutcome::default()),
|
||||
Arc::new(BlockExecutionOutput::default()),
|
||||
empty_trie_data(),
|
||||
));
|
||||
}
|
||||
@@ -847,7 +847,7 @@ fn test_tree_state_on_new_head_deep_fork() {
|
||||
for block in &chain_b {
|
||||
test_harness.tree.state.tree_state.insert_executed(ExecutedBlock::new(
|
||||
Arc::new(block.clone()),
|
||||
Arc::new(ExecutionOutcome::default()),
|
||||
Arc::new(BlockExecutionOutput::default()),
|
||||
empty_trie_data(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use alloy_primitives::{Address, B256, U256};
|
||||
use reth_primitives_traits::{Account, Bytecode};
|
||||
use revm::database::BundleState;
|
||||
|
||||
pub use alloy_evm::block::BlockExecutionResult;
|
||||
@@ -23,3 +25,36 @@ pub struct BlockExecutionOutput<T> {
|
||||
/// The changed state of the block after execution.
|
||||
pub state: BundleState,
|
||||
}
|
||||
|
||||
impl<T> BlockExecutionOutput<T> {
|
||||
/// Return bytecode if known.
|
||||
pub fn bytecode(&self, code_hash: &B256) -> Option<Bytecode> {
|
||||
self.state.bytecode(code_hash).map(Bytecode)
|
||||
}
|
||||
|
||||
/// Get account if account is known.
|
||||
pub fn account(&self, address: &Address) -> Option<Option<Account>> {
|
||||
self.state.account(address).map(|a| a.info.as_ref().map(Into::into))
|
||||
}
|
||||
|
||||
/// Get storage if value is known.
|
||||
///
|
||||
/// This means that depending on status we can potentially return `U256::ZERO`.
|
||||
pub fn storage(&self, address: &Address, storage_key: U256) -> Option<U256> {
|
||||
self.state.account(address).and_then(|a| a.storage_slot(storage_key))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for BlockExecutionOutput<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
result: BlockExecutionResult {
|
||||
receipts: Default::default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
state: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ use reth_evm::{
|
||||
execute::{BlockBuilder, BlockBuilderOutcome},
|
||||
ConfigureEvm,
|
||||
};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_primitives_traits::{
|
||||
AlloyBlockHeader, BlockTy, HeaderTy, NodePrimitives, ReceiptTy, Recovered,
|
||||
};
|
||||
use reth_execution_types::BlockExecutionOutput;
|
||||
use reth_primitives_traits::{BlockTy, HeaderTy, NodePrimitives, ReceiptTy, Recovered};
|
||||
use reth_revm::{cached::CachedReads, database::StateProviderDatabase, db::State};
|
||||
use reth_rpc_eth_types::{EthApiError, PendingBlock};
|
||||
use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, StateProviderFactory};
|
||||
@@ -112,12 +110,8 @@ where
|
||||
builder.finish(NoopProvider::default())?
|
||||
};
|
||||
|
||||
let execution_outcome = ExecutionOutcome::new(
|
||||
state.take_bundle(),
|
||||
vec![execution_result.receipts],
|
||||
block.number(),
|
||||
vec![execution_result.requests],
|
||||
);
|
||||
let execution_outcome =
|
||||
BlockExecutionOutput { state: state.take_bundle(), result: execution_result };
|
||||
|
||||
let pending_block = PendingBlock::with_executed_block(
|
||||
Instant::now() + Duration::from_secs(1),
|
||||
|
||||
@@ -18,7 +18,7 @@ use reth_evm::{
|
||||
op_revm::{constants::L1_BLOCK_CONTRACT, L1BlockInfo},
|
||||
ConfigureEvm, Database,
|
||||
};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_execution_types::BlockExecutionOutput;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::{transaction::OpTransaction, L2_TO_L1_MESSAGE_PASSER_ADDRESS};
|
||||
use reth_optimism_txpool::{
|
||||
@@ -375,12 +375,8 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
let sealed_block = Arc::new(block.sealed_block().clone());
|
||||
debug!(target: "payload_builder", id=%ctx.attributes().payload_id(), sealed_block_header = ?sealed_block.header(), "sealed built block");
|
||||
|
||||
let execution_outcome = ExecutionOutcome::new(
|
||||
db.take_bundle(),
|
||||
vec![execution_result.receipts],
|
||||
block.number(),
|
||||
Vec::new(),
|
||||
);
|
||||
let execution_outcome =
|
||||
BlockExecutionOutput { state: db.take_bundle(), result: execution_result };
|
||||
|
||||
// create the executed block data
|
||||
let executed: BuiltPayloadExecutedBlock<N> = BuiltPayloadExecutedBlock {
|
||||
|
||||
@@ -11,7 +11,7 @@ use alloy_rpc_types_engine::{PayloadAttributes as EthPayloadAttributes, PayloadI
|
||||
use core::fmt;
|
||||
use either::Either;
|
||||
use reth_chain_state::ComputedTrieData;
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_execution_types::BlockExecutionOutput;
|
||||
use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader};
|
||||
use reth_trie_common::{
|
||||
updates::{TrieUpdates, TrieUpdatesSorted},
|
||||
@@ -27,7 +27,7 @@ pub struct BuiltPayloadExecutedBlock<N: NodePrimitives> {
|
||||
/// Recovered Block
|
||||
pub recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
/// Block's execution outcome.
|
||||
pub execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
pub execution_output: Arc<BlockExecutionOutput<N::Receipt>>,
|
||||
/// Block's hashed state.
|
||||
///
|
||||
/// Supports both unsorted and sorted variants so payload builders can avoid cloning in order
|
||||
|
||||
@@ -12,7 +12,7 @@ use reth_chain_state::{BlockState, ComputedTrieData, ExecutedBlock};
|
||||
use reth_chainspec::{ChainSpecProvider, EthChainSpec};
|
||||
use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError, RethError};
|
||||
use reth_evm::{
|
||||
execute::{BlockBuilder, BlockBuilderOutcome, ExecutionOutcome},
|
||||
execute::{BlockBuilder, BlockBuilderOutcome, BlockExecutionOutput},
|
||||
ConfigureEvm, Evm, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_primitives_traits::{transaction::error::InvalidTransactionError, HeaderTy, SealedHeader};
|
||||
@@ -363,12 +363,8 @@ pub trait LoadPendingBlock:
|
||||
let BlockBuilderOutcome { execution_result, block, hashed_state, trie_updates } =
|
||||
builder.finish(NoopProvider::default()).map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
let execution_outcome = ExecutionOutcome::new(
|
||||
db.take_bundle(),
|
||||
vec![execution_result.receipts],
|
||||
block.number(),
|
||||
vec![execution_result.requests],
|
||||
);
|
||||
let execution_outcome =
|
||||
BlockExecutionOutput { state: db.take_bundle(), result: execution_result };
|
||||
|
||||
Ok(ExecutedBlock::new(
|
||||
block.into(),
|
||||
|
||||
@@ -99,9 +99,7 @@ impl<N: NodePrimitives> PendingBlock<N> {
|
||||
pub fn with_executed_block(expires_at: Instant, executed_block: ExecutedBlock<N>) -> Self {
|
||||
Self {
|
||||
expires_at,
|
||||
receipts: Arc::new(
|
||||
executed_block.execution_output.receipts.iter().flatten().cloned().collect(),
|
||||
),
|
||||
receipts: Arc::new(executed_block.execution_output.receipts.clone()),
|
||||
executed_block,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,7 +790,9 @@ mod tests {
|
||||
use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices};
|
||||
use reth_errors::ProviderError;
|
||||
use reth_ethereum_primitives::{Block, Receipt};
|
||||
use reth_execution_types::{Chain, ExecutionOutcome};
|
||||
use reth_execution_types::{
|
||||
BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome,
|
||||
};
|
||||
use reth_primitives_traits::{RecoveredBlock, SealedBlock, SignerRecoverable};
|
||||
use reth_storage_api::{
|
||||
BlockBodyIndicesProvider, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader,
|
||||
@@ -909,8 +911,15 @@ mod tests {
|
||||
.map(|block| {
|
||||
let senders = block.senders().expect("failed to recover senders");
|
||||
let block_receipts = receipts.get(block.number as usize).unwrap().clone();
|
||||
let execution_outcome =
|
||||
ExecutionOutcome { receipts: vec![block_receipts], ..Default::default() };
|
||||
let execution_outcome = BlockExecutionOutput {
|
||||
result: BlockExecutionResult {
|
||||
receipts: block_receipts,
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
state: BundleState::default(),
|
||||
};
|
||||
|
||||
ExecutedBlock {
|
||||
recovered_block: Arc::new(RecoveredBlock::new_sealed(
|
||||
@@ -979,8 +988,7 @@ mod tests {
|
||||
state.parent_state_chain().last().expect("qed").block();
|
||||
let num_hash = lowest_memory_block.recovered_block().num_hash();
|
||||
|
||||
let mut execution_output = (*lowest_memory_block.execution_output).clone();
|
||||
execution_output.first_block = lowest_memory_block.recovered_block().number;
|
||||
let execution_output = (*lowest_memory_block.execution_output).clone();
|
||||
lowest_memory_block.execution_output = Arc::new(execution_output);
|
||||
|
||||
// Push to disk
|
||||
@@ -1708,8 +1716,8 @@ mod tests {
|
||||
block.clone(),
|
||||
senders,
|
||||
)),
|
||||
execution_output: Arc::new(ExecutionOutcome {
|
||||
bundle: BundleState::new(
|
||||
execution_output: Arc::new(BlockExecutionOutput {
|
||||
state: BundleState::new(
|
||||
in_memory_state.into_iter().map(|(address, (account, _))| {
|
||||
(address, None, Some(account.into()), Default::default())
|
||||
}),
|
||||
@@ -1718,8 +1726,12 @@ mod tests {
|
||||
})],
|
||||
[],
|
||||
),
|
||||
first_block: first_in_memory_block,
|
||||
..Default::default()
|
||||
result: BlockExecutionResult {
|
||||
receipts: Default::default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -1307,7 +1307,7 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
|
||||
let changesets = state
|
||||
.block()
|
||||
.execution_output
|
||||
.bundle
|
||||
.state
|
||||
.reverts
|
||||
.clone()
|
||||
.to_plain_state_reverts()
|
||||
@@ -1360,7 +1360,7 @@ impl<N: ProviderNodeTypes> ChangeSetReader for ConsistentProvider<N> {
|
||||
let changesets = state
|
||||
.block_ref()
|
||||
.execution_output
|
||||
.bundle
|
||||
.state
|
||||
.reverts
|
||||
.clone()
|
||||
.to_plain_state_reverts()
|
||||
@@ -1406,7 +1406,7 @@ impl<N: ProviderNodeTypes> ChangeSetReader for ConsistentProvider<N> {
|
||||
let changeset = state
|
||||
.block_ref()
|
||||
.execution_output
|
||||
.bundle
|
||||
.state
|
||||
.reverts
|
||||
.clone()
|
||||
.to_plain_state_reverts()
|
||||
@@ -1460,7 +1460,7 @@ impl<N: ProviderNodeTypes> ChangeSetReader for ConsistentProvider<N> {
|
||||
let block_changesets = state
|
||||
.block_ref()
|
||||
.execution_output
|
||||
.bundle
|
||||
.state
|
||||
.reverts
|
||||
.clone()
|
||||
.to_plain_state_reverts()
|
||||
@@ -1508,7 +1508,7 @@ impl<N: ProviderNodeTypes> ChangeSetReader for ConsistentProvider<N> {
|
||||
count += state
|
||||
.block_ref()
|
||||
.execution_output
|
||||
.bundle
|
||||
.state
|
||||
.reverts
|
||||
.clone()
|
||||
.to_plain_state_reverts()
|
||||
@@ -1551,7 +1551,7 @@ impl<N: ProviderNodeTypes> StateReader for ConsistentProvider<N> {
|
||||
) -> ProviderResult<Option<ExecutionOutcome<Self::Receipt>>> {
|
||||
if let Some(state) = self.head_block.as_ref().and_then(|b| b.block_on_chain(block.into())) {
|
||||
let state = state.block_ref().execution_outcome().clone();
|
||||
Ok(Some(state))
|
||||
Ok(Some(ExecutionOutcome::from((state, block))))
|
||||
} else {
|
||||
Self::get_state(self, block..=block)
|
||||
}
|
||||
@@ -1571,7 +1571,7 @@ mod tests {
|
||||
use reth_chain_state::{ExecutedBlock, NewCanonicalChain};
|
||||
use reth_db_api::models::AccountBeforeTx;
|
||||
use reth_ethereum_primitives::Block;
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_execution_types::{BlockExecutionOutput, BlockExecutionResult, ExecutionOutcome};
|
||||
use reth_primitives_traits::{RecoveredBlock, SealedBlock};
|
||||
use reth_storage_api::{BlockReader, BlockSource, ChangeSetReader};
|
||||
use reth_testing_utils::generators::{
|
||||
@@ -1883,8 +1883,8 @@ mod tests {
|
||||
block.clone(),
|
||||
senders,
|
||||
)),
|
||||
execution_output: Arc::new(ExecutionOutcome {
|
||||
bundle: BundleState::new(
|
||||
execution_output: Arc::new(BlockExecutionOutput {
|
||||
state: BundleState::new(
|
||||
in_memory_state.into_iter().map(|(address, (account, _))| {
|
||||
(address, None, Some(account.into()), Default::default())
|
||||
}),
|
||||
@@ -1893,8 +1893,12 @@ mod tests {
|
||||
})],
|
||||
[],
|
||||
),
|
||||
first_block: first_in_memory_block,
|
||||
..Default::default()
|
||||
result: BlockExecutionResult {
|
||||
receipts: Default::default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ use reth_db_api::{
|
||||
transaction::{DbTx, DbTxMut},
|
||||
BlockNumberList, PlainAccountState, PlainStorageState,
|
||||
};
|
||||
use reth_execution_types::{Chain, ExecutionOutcome};
|
||||
use reth_execution_types::{BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome};
|
||||
use reth_node_types::{BlockTy, BodyTy, HeaderTy, NodeTypes, ReceiptTy, TxTy};
|
||||
use reth_primitives_traits::{
|
||||
Account, Block as _, BlockBody as _, Bytecode, RecoveredBlock, SealedHeader, StorageEntry,
|
||||
@@ -60,7 +60,7 @@ use reth_static_file_types::StaticFileSegment;
|
||||
use reth_storage_api::{
|
||||
BlockBodyIndicesProvider, BlockBodyReader, MetadataProvider, MetadataWriter,
|
||||
NodePrimitivesProvider, StateProvider, StateWriteConfig, StorageChangeSetReader,
|
||||
StorageSettingsCache, TryIntoHistoricalStateProvider,
|
||||
StorageSettingsCache, TryIntoHistoricalStateProvider, WriteStateInput,
|
||||
};
|
||||
use reth_storage_errors::provider::{ProviderResult, StaticFileWriterError};
|
||||
use reth_trie::{
|
||||
@@ -537,7 +537,10 @@ impl<TX: DbTx + DbTxMut + 'static, N: NodeTypesForProvider> DatabaseProvider<TX,
|
||||
// Skip receipts/account changesets if they're being written to static files.
|
||||
let start = Instant::now();
|
||||
self.write_state(
|
||||
execution_output,
|
||||
WriteStateInput::Single {
|
||||
outcome: execution_output,
|
||||
block: recovered_block.number(),
|
||||
},
|
||||
OriginalValuesKnown::No,
|
||||
StateWriteConfig {
|
||||
write_receipts: !sf_ctx.write_receipts,
|
||||
@@ -2037,16 +2040,17 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateWriter
|
||||
type Receipt = ReceiptTy<N>;
|
||||
|
||||
#[instrument(level = "debug", target = "providers::db", skip_all)]
|
||||
fn write_state(
|
||||
fn write_state<'a>(
|
||||
&self,
|
||||
execution_outcome: &ExecutionOutcome<Self::Receipt>,
|
||||
execution_outcome: impl Into<WriteStateInput<'a, Self::Receipt>>,
|
||||
is_value_known: OriginalValuesKnown,
|
||||
config: StateWriteConfig,
|
||||
) -> ProviderResult<()> {
|
||||
let execution_outcome = execution_outcome.into();
|
||||
let first_block = execution_outcome.first_block();
|
||||
|
||||
let (plain_state, reverts) =
|
||||
execution_outcome.bundle.to_plain_state_and_reverts(is_value_known);
|
||||
execution_outcome.state().to_plain_state_and_reverts(is_value_known);
|
||||
|
||||
self.write_state_reverts(reverts, first_block, config)?;
|
||||
self.write_state_changes(plain_state)?;
|
||||
@@ -2101,7 +2105,7 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateWriter
|
||||
}
|
||||
|
||||
for (idx, (receipts, first_tx_index)) in
|
||||
execution_outcome.receipts.iter().zip(block_indices).enumerate()
|
||||
execution_outcome.receipts().zip(block_indices).enumerate()
|
||||
{
|
||||
let block_number = first_block + idx as u64;
|
||||
|
||||
@@ -3130,12 +3134,15 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockWriter
|
||||
// Wrap block in ExecutedBlock with empty execution output (no receipts/state/trie)
|
||||
let executed_block = ExecutedBlock::new(
|
||||
Arc::new(block.clone()),
|
||||
Arc::new(ExecutionOutcome::new(
|
||||
Default::default(),
|
||||
Vec::<Vec<ReceiptTy<N>>>::new(),
|
||||
block_number,
|
||||
vec![],
|
||||
)),
|
||||
Arc::new(BlockExecutionOutput {
|
||||
result: BlockExecutionResult {
|
||||
receipts: Default::default(),
|
||||
requests: Default::default(),
|
||||
gas_used: 0,
|
||||
blob_gas_used: 0,
|
||||
},
|
||||
state: Default::default(),
|
||||
}),
|
||||
ComputedTrieData::default(),
|
||||
);
|
||||
|
||||
|
||||
@@ -587,7 +587,7 @@ impl RocksDBProvider {
|
||||
let mut account_history: BTreeMap<Address, Vec<u64>> = BTreeMap::new();
|
||||
for (block_idx, block) in blocks.iter().enumerate() {
|
||||
let block_number = ctx.first_block_number + block_idx as u64;
|
||||
let bundle = &block.execution_outcome().bundle;
|
||||
let bundle = &block.execution_outcome().state;
|
||||
for &address in bundle.state().keys() {
|
||||
account_history.entry(address).or_default().push(block_number);
|
||||
}
|
||||
@@ -612,7 +612,7 @@ impl RocksDBProvider {
|
||||
let mut storage_history: BTreeMap<(Address, B256), Vec<u64>> = BTreeMap::new();
|
||||
for (block_idx, block) in blocks.iter().enumerate() {
|
||||
let block_number = ctx.first_block_number + block_idx as u64;
|
||||
let bundle = &block.execution_outcome().bundle;
|
||||
let bundle = &block.execution_outcome().state;
|
||||
for (&address, account) in bundle.state() {
|
||||
for &slot in account.storage.keys() {
|
||||
let key = B256::new(slot.to_be_bytes());
|
||||
|
||||
@@ -594,7 +594,7 @@ impl<N: NodePrimitives> StaticFileProvider<N> {
|
||||
continue
|
||||
}
|
||||
|
||||
for (i, receipt) in block.execution_outcome().receipts.iter().flatten().enumerate() {
|
||||
for (i, receipt) in block.execution_outcome().receipts.iter().enumerate() {
|
||||
w.append_receipt(first_tx + i as u64, receipt)?;
|
||||
}
|
||||
}
|
||||
@@ -609,7 +609,7 @@ impl<N: NodePrimitives> StaticFileProvider<N> {
|
||||
) -> ProviderResult<()> {
|
||||
for block in blocks {
|
||||
let block_number = block.recovered_block().number();
|
||||
let reverts = block.execution_outcome().bundle.reverts.to_plain_state_reverts();
|
||||
let reverts = block.execution_outcome().state.reverts.to_plain_state_reverts();
|
||||
|
||||
for account_block_reverts in reverts.accounts {
|
||||
let changeset = account_block_reverts
|
||||
|
||||
@@ -1,23 +1,98 @@
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::transaction::Either;
|
||||
use alloy_primitives::BlockNumber;
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
|
||||
use reth_storage_errors::provider::ProviderResult;
|
||||
use reth_trie_common::HashedPostStateSorted;
|
||||
use revm_database::{
|
||||
states::{PlainStateReverts, StateChangeset},
|
||||
OriginalValuesKnown,
|
||||
BundleState, OriginalValuesKnown,
|
||||
};
|
||||
|
||||
/// A helper type used as input to [`StateWriter`] for writing execution outcome for one or many
|
||||
/// blocks.
|
||||
#[derive(Debug)]
|
||||
pub enum WriteStateInput<'a, R> {
|
||||
/// A single block execution outcome.
|
||||
Single {
|
||||
/// The execution outcome.
|
||||
outcome: &'a BlockExecutionOutput<R>,
|
||||
/// Block number
|
||||
block: BlockNumber,
|
||||
},
|
||||
/// Multiple block execution outcomes.
|
||||
Multiple(&'a ExecutionOutcome<R>),
|
||||
}
|
||||
|
||||
impl<'a, R> WriteStateInput<'a, R> {
|
||||
/// Number of blocks in the execution outcome.
|
||||
pub const fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::Single { .. } => 1,
|
||||
Self::Multiple(outcome) => outcome.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the execution outcome is empty.
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Single { outcome, .. } => outcome.result.receipts.is_empty(),
|
||||
Self::Multiple(outcome) => outcome.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of the first block.
|
||||
pub const fn first_block(&self) -> BlockNumber {
|
||||
match self {
|
||||
Self::Single { block, .. } => *block,
|
||||
Self::Multiple(outcome) => outcome.first_block(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of the last block.
|
||||
pub const fn last_block(&self) -> BlockNumber {
|
||||
match self {
|
||||
Self::Single { block, .. } => *block,
|
||||
Self::Multiple(outcome) => outcome.last_block(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`BundleState`].
|
||||
pub const fn state(&self) -> &BundleState {
|
||||
match self {
|
||||
Self::Single { outcome, .. } => &outcome.state,
|
||||
Self::Multiple(outcome) => &outcome.bundle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over receipt sets for each block.
|
||||
pub fn receipts(&self) -> impl Iterator<Item = &Vec<R>> {
|
||||
match self {
|
||||
Self::Single { outcome, .. } => {
|
||||
Either::Left(core::iter::once(&outcome.result.receipts))
|
||||
}
|
||||
Self::Multiple(outcome) => Either::Right(outcome.receipts.iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> From<&'a ExecutionOutcome<R>> for WriteStateInput<'a, R> {
|
||||
fn from(outcome: &'a ExecutionOutcome<R>) -> Self {
|
||||
Self::Multiple(outcome)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait specifically for writing state changes or reverts
|
||||
pub trait StateWriter {
|
||||
/// Receipt type included into [`ExecutionOutcome`].
|
||||
type Receipt;
|
||||
type Receipt: 'static;
|
||||
|
||||
/// Write the state and optionally receipts to the database.
|
||||
///
|
||||
/// Use `config` to skip writing certain data types when they are written elsewhere.
|
||||
fn write_state(
|
||||
fn write_state<'a>(
|
||||
&self,
|
||||
execution_outcome: &ExecutionOutcome<Self::Receipt>,
|
||||
execution_outcome: impl Into<WriteStateInput<'a, Self::Receipt>>,
|
||||
is_value_known: OriginalValuesKnown,
|
||||
config: StateWriteConfig,
|
||||
) -> ProviderResult<()>;
|
||||
|
||||
Reference in New Issue
Block a user