1use crate::{ExecutorError, ExecutorResult, TrieDB, TrieDBError, TrieDBProvider};
4use alloc::{string::ToString, vec::Vec};
5use alloy_consensus::{Header, Sealed, crypto::RecoveryError};
6use alloy_evm::{
7 EvmFactory, FromRecoveredTx, FromTxWithEncoded,
8 block::{BlockExecutionResult, BlockExecutor, BlockExecutorFactory},
9};
10use alloy_op_evm::{OpBlockExecutionCtx, OpBlockExecutorFactory, block::OpAlloyReceiptBuilder};
11use core::fmt::Debug;
12use kona_genesis::RollupConfig;
13use kona_mpt::TrieHinter;
14use op_alloy_consensus::{OpReceiptEnvelope, OpTxEnvelope};
15use op_alloy_rpc_types_engine::OpPayloadAttributes;
16use op_revm::OpSpecId;
17use revm::database::{State, states::bundle_state::BundleRetention};
18
19#[derive(Debug)]
22pub struct StatelessL2Builder<'a, P, H, Evm>
23where
24 P: TrieDBProvider,
25 H: TrieHinter,
26 Evm: EvmFactory,
27{
28 pub(crate) config: &'a RollupConfig,
30 pub(crate) trie_db: TrieDB<P, H>,
32 pub(crate) factory: OpBlockExecutorFactory<OpAlloyReceiptBuilder, RollupConfig, Evm>,
35}
36
37impl<'a, P, H, Evm> StatelessL2Builder<'a, P, H, Evm>
38where
39 P: TrieDBProvider + Debug,
40 H: TrieHinter + Debug,
41 Evm: EvmFactory<Spec = OpSpecId> + 'static,
42 <Evm as EvmFactory>::Tx: FromTxWithEncoded<OpTxEnvelope> + FromRecoveredTx<OpTxEnvelope>,
43{
44 pub fn new(
46 config: &'a RollupConfig,
47 evm_factory: Evm,
48 provider: P,
49 hinter: H,
50 parent_header: Sealed<Header>,
51 ) -> Self {
52 let trie_db = TrieDB::new(parent_header, provider, hinter);
53 let factory = OpBlockExecutorFactory::new(
54 OpAlloyReceiptBuilder::default(),
55 config.clone(),
56 evm_factory,
57 );
58 Self { config, trie_db, factory }
59 }
60
61 pub fn build_block(
63 &mut self,
64 attrs: OpPayloadAttributes,
65 ) -> ExecutorResult<BlockBuildingOutcome> {
66 let base_fee_params =
68 Self::active_base_fee_params(self.config, self.trie_db.parent_block_header(), &attrs)?;
69 let evm_env = self.evm_env(
70 self.config.spec_id(attrs.payload_attributes.timestamp),
71 self.trie_db.parent_block_header(),
72 &attrs,
73 &base_fee_params,
74 )?;
75 let block_env = evm_env.block_env().clone();
76 let parent_hash = self.trie_db.parent_block_header().seal();
77
78 self.trie_db
83 .hinter
84 .hint_execution_witness(parent_hash, &attrs)
85 .map_err(|e| TrieDBError::Provider(e.to_string()))?;
86
87 info!(
88 target: "block_builder",
89 block_number = %block_env.number,
90 block_timestamp = %block_env.timestamp,
91 block_gas_limit = block_env.gas_limit,
92 transactions = attrs.transactions.as_ref().map_or(0, |txs| txs.len()),
93 "Beginning block building."
94 );
95
96 let mut state = State::builder()
98 .with_database(&mut self.trie_db)
99 .with_bundle_update()
100 .without_state_clear()
101 .build();
102 let evm = self.factory.evm_factory().create_evm(&mut state, evm_env);
103 let ctx = OpBlockExecutionCtx {
104 parent_hash,
105 parent_beacon_block_root: attrs.payload_attributes.parent_beacon_block_root,
106 extra_data: Default::default(),
108 };
109 let executor = self.factory.create_executor(evm, ctx);
110
111 let transactions = attrs
113 .recovered_transactions_with_encoded()
114 .collect::<Result<Vec<_>, RecoveryError>>()
115 .map_err(ExecutorError::Recovery)?;
116 let ex_result = executor.execute_block(transactions.iter())?;
117
118 info!(
119 target: "block_builder",
120 gas_used = ex_result.gas_used,
121 gas_limit = block_env.gas_limit,
122 "Finished block building. Beginning sealing job."
123 );
124
125 state.merge_transitions(BundleRetention::Reverts);
127 let bundle = state.take_bundle();
128 let header = self.seal_block(&attrs, parent_hash, &block_env, &ex_result, bundle)?;
129
130 info!(
131 target: "block_builder",
132 number = header.number,
133 hash = ?header.seal(),
134 state_root = ?header.state_root,
135 transactions_root = ?header.transactions_root,
136 receipts_root = ?header.receipts_root,
137 "Sealed new block",
138 );
139
140 self.trie_db.set_parent_block_header(header.clone());
142 Ok((header, ex_result).into())
143 }
144}
145
146#[derive(Debug, Clone)]
149pub struct BlockBuildingOutcome {
150 pub header: Sealed<Header>,
152 pub execution_result: BlockExecutionResult<OpReceiptEnvelope>,
154}
155
156impl From<(Sealed<Header>, BlockExecutionResult<OpReceiptEnvelope>)> for BlockBuildingOutcome {
157 fn from(
158 (header, execution_result): (Sealed<Header>, BlockExecutionResult<OpReceiptEnvelope>),
159 ) -> Self {
160 Self { header, execution_result }
161 }
162}
163
164#[cfg(test)]
165mod test {
166 use crate::test_utils::run_test_fixture;
167 use rstest::rstest;
168 use std::path::PathBuf;
169
170 #[rstest]
171 #[tokio::test]
172 async fn test_statelessly_execute_block(
173 #[base_dir = "./testdata"]
174 #[files("*.tar.gz")]
175 path: PathBuf,
176 ) {
177 run_test_fixture(path).await;
178 }
179}