reth_optimism_flashblocks/
worker.rs1use crate::PendingFlashBlock;
2use alloy_eips::{eip2718::WithEncoded, BlockNumberOrTag};
3use alloy_primitives::B256;
4use op_alloy_rpc_types_engine::OpFlashblockPayloadBase;
5use reth_chain_state::{ComputedTrieData, ExecutedBlock};
6use reth_errors::RethError;
7use reth_evm::{
8 execute::{BlockBuilder, BlockBuilderOutcome},
9 ConfigureEvm,
10};
11use reth_execution_types::ExecutionOutcome;
12use reth_primitives_traits::{
13 AlloyBlockHeader, BlockTy, HeaderTy, NodePrimitives, ReceiptTy, Recovered,
14};
15use reth_revm::{cached::CachedReads, database::StateProviderDatabase, db::State};
16use reth_rpc_eth_types::{EthApiError, PendingBlock};
17use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, StateProviderFactory};
18use std::{
19 sync::Arc,
20 time::{Duration, Instant},
21};
22use tracing::trace;
23
24#[derive(Debug)]
26pub(crate) struct FlashBlockBuilder<EvmConfig, Provider> {
27 evm_config: EvmConfig,
28 provider: Provider,
29}
30
31impl<EvmConfig, Provider> FlashBlockBuilder<EvmConfig, Provider> {
32 pub(crate) const fn new(evm_config: EvmConfig, provider: Provider) -> Self {
33 Self { evm_config, provider }
34 }
35
36 pub(crate) const fn provider(&self) -> &Provider {
37 &self.provider
38 }
39}
40
41pub(crate) struct BuildArgs<I> {
42 pub(crate) base: OpFlashblockPayloadBase,
43 pub(crate) transactions: I,
44 pub(crate) cached_state: Option<(B256, CachedReads)>,
45 pub(crate) last_flashblock_index: u64,
46 pub(crate) last_flashblock_hash: B256,
47 pub(crate) compute_state_root: bool,
48}
49
50impl<N, EvmConfig, Provider> FlashBlockBuilder<EvmConfig, Provider>
51where
52 N: NodePrimitives,
53 EvmConfig: ConfigureEvm<Primitives = N, NextBlockEnvCtx: From<OpFlashblockPayloadBase> + Unpin>,
54 Provider: StateProviderFactory
55 + BlockReaderIdExt<
56 Header = HeaderTy<N>,
57 Block = BlockTy<N>,
58 Transaction = N::SignedTx,
59 Receipt = ReceiptTy<N>,
60 > + Unpin,
61{
62 pub(crate) fn execute<I: IntoIterator<Item = WithEncoded<Recovered<N::SignedTx>>>>(
67 &self,
68 mut args: BuildArgs<I>,
69 ) -> eyre::Result<Option<(PendingFlashBlock<N>, CachedReads)>> {
70 trace!(target: "flashblocks", "Attempting new pending block from flashblocks");
71
72 let latest = self
73 .provider
74 .latest_header()?
75 .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?;
76 let latest_hash = latest.hash();
77
78 if args.base.parent_hash != latest_hash {
79 trace!(target: "flashblocks", flashblock_parent = ?args.base.parent_hash, local_latest=?latest.num_hash(),"Skipping non consecutive flashblock");
80 return Ok(None)
82 }
83
84 let state_provider = self.provider.history_by_block_hash(latest.hash())?;
85
86 let mut request_cache = args
87 .cached_state
88 .take()
89 .filter(|(hash, _)| hash == &latest_hash)
90 .map(|(_, state)| state)
91 .unwrap_or_default();
92 let cached_db = request_cache.as_db_mut(StateProviderDatabase::new(&state_provider));
93 let mut state = State::builder().with_database(cached_db).with_bundle_update().build();
94
95 let mut builder = self
96 .evm_config
97 .builder_for_next_block(&mut state, &latest, args.base.into())
98 .map_err(RethError::other)?;
99
100 builder.apply_pre_execution_changes()?;
101
102 for tx in args.transactions {
103 let _gas_used = builder.execute_transaction(tx)?;
104 }
105
106 let BlockBuilderOutcome { execution_result, block, hashed_state, .. } =
108 if args.compute_state_root {
109 trace!(target: "flashblocks", "Computing block state root");
110 builder.finish(&state_provider)?
111 } else {
112 builder.finish(NoopProvider::default())?
113 };
114
115 let execution_outcome = ExecutionOutcome::new(
116 state.take_bundle(),
117 vec![execution_result.receipts],
118 block.number(),
119 vec![execution_result.requests],
120 );
121
122 let pending_block = PendingBlock::with_executed_block(
123 Instant::now() + Duration::from_secs(1),
124 ExecutedBlock::new(
125 block.into(),
126 Arc::new(execution_outcome),
127 ComputedTrieData::without_trie_input(
128 Arc::new(hashed_state.into_sorted()),
129 Arc::default(),
130 ),
131 ),
132 );
133 let pending_flashblock = PendingFlashBlock::new(
134 pending_block,
135 args.last_flashblock_index,
136 args.last_flashblock_hash,
137 args.compute_state_root,
138 );
139
140 Ok(Some((pending_flashblock, request_cache)))
141 }
142}
143
144impl<EvmConfig: Clone, Provider: Clone> Clone for FlashBlockBuilder<EvmConfig, Provider> {
145 fn clone(&self) -> Self {
146 Self { evm_config: self.evm_config.clone(), provider: self.provider.clone() }
147 }
148}