1- use alloy_consensus:: { Transaction , TxEip1559 } ;
1+ use alloy_consensus:: TxEip1559 ;
22use alloy_eips:: { Encodable2718 , eip7623:: TOTAL_COST_FLOOR_PER_TOKEN } ;
33use alloy_evm:: Database ;
44use alloy_op_evm:: OpEvm ;
55use alloy_primitives:: {
66 Address , B256 , Bytes , TxKind , U256 ,
77 map:: foldhash:: { HashMap , HashSet , HashSetExt } ,
88} ;
9+ use alloy_sol_types:: { ContractError , Error , Revert , SolCall , SolError , SolInterface } ;
910use core:: fmt:: Debug ;
1011use op_alloy_consensus:: OpTypedTransaction ;
12+ use op_alloy_rpc_types:: OpTransactionRequest ;
1113use op_revm:: { OpHaltReason , OpTransactionError } ;
1214use reth_evm:: {
1315 ConfigureEvm , Evm , EvmError , eth:: receipt_builder:: ReceiptBuilderCtx ,
@@ -18,22 +20,27 @@ use reth_optimism_primitives::OpTransactionSigned;
1820use reth_primitives:: Recovered ;
1921use reth_provider:: { ProviderError , StateProvider } ;
2022use reth_revm:: { State , database:: StateProviderDatabase } ;
23+ use reth_rpc_api:: eth:: { EthTxEnvError , transaction:: TryIntoTxEnv } ;
2124use revm:: {
22- DatabaseCommit ,
23- context:: result:: { EVMError , ExecutionResult , ResultAndState } ,
25+ DatabaseCommit , DatabaseRef ,
26+ context:: {
27+ ContextTr ,
28+ result:: { EVMError , ExecutionResult , ResultAndState } ,
29+ } ,
2430 inspector:: NoOpInspector ,
2531 state:: Account ,
2632} ;
33+ use std:: borrow:: Cow ;
2734use tracing:: warn;
2835
2936use crate :: {
3037 builders:: context:: OpPayloadBuilderCtx , primitives:: reth:: ExecutionInfo , tx_signer:: Signer ,
3138} ;
3239
3340#[ derive( Debug , Default ) ]
34- pub struct SimulationSuccessResult {
41+ pub struct SimulationSuccessResult < T : SolCall > {
3542 pub gas_used : u64 ,
36- pub output : Bytes ,
43+ pub output : T :: Return ,
3744 pub state_changes : HashMap < Address , Account > ,
3845}
3946
@@ -61,8 +68,8 @@ impl BuilderTransactionCtx {
6168
6269#[ derive( Debug , thiserror:: Error ) ]
6370pub enum InvalidContractDataError {
64- #[ error( "did not find expected log {0:?} in emitted logs " ) ]
65- InvalidLogs ( B256 ) ,
71+ #[ error( "did not find expected logs expected {0:?} but got {1:?} " ) ]
72+ InvalidLogs ( Vec < B256 > , Vec < B256 > ) ,
6673 #[ error( "could not decode output from contract call" ) ]
6774 OutputAbiDecodeError ,
6875}
@@ -80,11 +87,11 @@ pub enum BuilderTransactionError {
8087 #[ error( "contract {0} may be incorrect, invalid contract data: {1}" ) ]
8188 InvalidContract ( Address , InvalidContractDataError ) ,
8289 /// Transaction halted execution
83- #[ error( "transaction halted {0:?}" ) ]
84- TransactionHalted ( OpHaltReason ) ,
90+ #[ error( "transaction to {0} halted {1 :?}" ) ]
91+ TransactionHalted ( Address , OpHaltReason ) ,
8592 /// Transaction reverted
86- #[ error( "transaction reverted {0}" ) ]
87- TransactionReverted ( Box < dyn core :: error :: Error + Send + Sync > ) ,
93+ #[ error( "transaction to {0} reverted {1 }" ) ]
94+ TransactionReverted ( Address , Revert ) ,
8895 /// Invalid tx errors during evm execution.
8996 #[ error( "invalid transaction error {0}" ) ]
9097 InvalidTransactionError ( Box < dyn core:: error:: Error + Send + Sync > ) ,
@@ -108,6 +115,12 @@ impl From<EVMError<ProviderError, OpTransactionError>> for BuilderTransactionErr
108115 }
109116}
110117
118+ impl From < EthTxEnvError > for BuilderTransactionError {
119+ fn from ( error : EthTxEnvError ) -> Self {
120+ BuilderTransactionError :: EvmExecutionError ( Box :: new ( error) )
121+ }
122+ }
123+
111124impl From < BuilderTransactionError > for PayloadBuilderError {
112125 fn from ( error : BuilderTransactionError ) -> Self {
113126 match error {
@@ -130,15 +143,35 @@ impl BuilderTransactionError {
130143}
131144
132145pub trait BuilderTransactions < ExtraCtx : Debug + Default = ( ) , Extra : Debug + Default = ( ) > {
146+ // Simulates and returns the signed builder transactions. The simulation modifies and commit
147+ // changes to the db so call new_simulation_state to simulate on a new copy of the state
133148 fn simulate_builder_txs (
134149 & self ,
135150 state_provider : impl StateProvider + Clone ,
136151 info : & mut ExecutionInfo < Extra > ,
137152 ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
138- db : & mut State < impl Database > ,
153+ db : & mut State < impl Database + DatabaseRef > ,
139154 top_of_block : bool ,
140155 ) -> Result < Vec < BuilderTransactionCtx > , BuilderTransactionError > ;
141156
157+ fn simulate_builder_txs_with_new_state (
158+ & self ,
159+ state_provider : impl StateProvider + Clone ,
160+ info : & mut ExecutionInfo < Extra > ,
161+ ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
162+ db : & State < impl Database > ,
163+ top_of_block : bool ,
164+ ) -> Result < Vec < BuilderTransactionCtx > , BuilderTransactionError > {
165+ let mut simulation_state = self . new_simulation_state ( state_provider. clone ( ) , db) ;
166+ self . simulate_builder_txs (
167+ state_provider,
168+ info,
169+ ctx,
170+ & mut simulation_state,
171+ top_of_block,
172+ )
173+ }
174+
142175 fn add_builder_txs (
143176 & self ,
144177 state_provider : impl StateProvider + Clone ,
@@ -148,19 +181,20 @@ pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Def
148181 top_of_block : bool ,
149182 ) -> Result < Vec < BuilderTransactionCtx > , BuilderTransactionError > {
150183 {
184+ let builder_txs = self . simulate_builder_txs_with_new_state (
185+ state_provider,
186+ info,
187+ builder_ctx,
188+ db,
189+ top_of_block,
190+ ) ?;
191+
151192 let mut evm = builder_ctx
152193 . evm_config
153194 . evm_with_env ( & mut * db, builder_ctx. evm_env . clone ( ) ) ;
154195
155196 let mut invalid: HashSet < Address > = HashSet :: new ( ) ;
156197
157- let builder_txs = self . simulate_builder_txs (
158- state_provider,
159- info,
160- builder_ctx,
161- evm. db_mut ( ) ,
162- top_of_block,
163- ) ?;
164198 for builder_tx in builder_txs. iter ( ) {
165199 if builder_tx. is_top_of_block != top_of_block {
166200 // don't commit tx if the buidler tx is not being added in the intended
@@ -211,52 +245,37 @@ pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Def
211245 }
212246 }
213247
214- fn simulate_builder_txs_state (
248+ // Creates a copy of the state to simulate against
249+ fn new_simulation_state (
215250 & self ,
216- state_provider : impl StateProvider + Clone ,
217- builder_txs : Vec < & BuilderTransactionCtx > ,
218- ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
219- db : & mut State < impl Database > ,
220- ) -> Result < State < StateProviderDatabase < impl StateProvider > > , BuilderTransactionError > {
221- let state = StateProviderDatabase :: new ( state_provider. clone ( ) ) ;
222- let mut simulation_state = State :: builder ( )
251+ state_provider : impl StateProvider ,
252+ db : & State < impl Database > ,
253+ ) -> State < StateProviderDatabase < impl StateProvider > > {
254+ let state = StateProviderDatabase :: new ( state_provider) ;
255+
256+ State :: builder ( )
223257 . with_database ( state)
224258 . with_cached_prestate ( db. cache . clone ( ) )
225259 . with_bundle_update ( )
226- . build ( ) ;
227- let mut evm = ctx
228- . evm_config
229- . evm_with_env ( & mut simulation_state, ctx. evm_env . clone ( ) ) ;
230-
231- for builder_tx in builder_txs {
232- let ResultAndState { state, .. } = evm
233- . transact ( & builder_tx. signed_tx )
234- . map_err ( |err| BuilderTransactionError :: EvmExecutionError ( Box :: new ( err) ) ) ?;
235-
236- evm. db_mut ( ) . commit ( state) ;
237- }
238-
239- Ok ( simulation_state)
260+ . build ( )
240261 }
241262
242263 fn sign_tx (
243264 & self ,
244265 to : Address ,
245266 from : Signer ,
246- gas_used : Option < u64 > ,
267+ gas_used : u64 ,
247268 calldata : Bytes ,
248269 ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
249- db : & mut State < impl Database > ,
270+ db : impl DatabaseRef ,
250271 ) -> Result < Recovered < OpTransactionSigned > , BuilderTransactionError > {
251272 let nonce = get_nonce ( db, from. address ) ?;
252273 // Create the EIP-1559 transaction
253274 let tx = OpTypedTransaction :: Eip1559 ( TxEip1559 {
254275 chain_id : ctx. chain_id ( ) ,
255276 nonce,
256277 // Due to EIP-150, 63/64 of available gas is forwarded to external calls so need to add a buffer
257- gas_limit : gas_used
258- . map ( |gas| gas * 64 / 63 )
259- . unwrap_or ( ctx. block_gas_limit ( ) ) ,
278+ gas_limit : gas_used * 64 / 63 ,
260279 max_fee_per_gas : ctx. base_fee ( ) . into ( ) ,
261280 to : TxKind :: Call ( to) ,
262281 input : calldata,
@@ -265,18 +284,32 @@ pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Def
265284 Ok ( from. sign_tx ( tx) ?)
266285 }
267286
268- fn simulate_call (
287+ fn commit_txs (
288+ & self ,
289+ signed_txs : Vec < Recovered < OpTransactionSigned > > ,
290+ ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
291+ db : & mut State < impl Database > ,
292+ ) -> Result < ( ) , BuilderTransactionError > {
293+ let mut evm = ctx. evm_config . evm_with_env ( & mut * db, ctx. evm_env . clone ( ) ) ;
294+ for signed_tx in signed_txs {
295+ let ResultAndState { state, .. } = evm
296+ . transact ( & signed_tx)
297+ . map_err ( |err| BuilderTransactionError :: EvmExecutionError ( Box :: new ( err) ) ) ?;
298+ evm. db_mut ( ) . commit ( state)
299+ }
300+ Ok ( ( ) )
301+ }
302+
303+ fn simulate_call < T : SolCall , E : SolInterface > (
269304 & self ,
270- signed_tx : Recovered < OpTransactionSigned > ,
271- expected_topic : Option < B256 > ,
272- revert_handler : impl FnOnce ( Bytes ) -> BuilderTransactionError ,
273- evm : & mut OpEvm <
274- & mut State < StateProviderDatabase < impl StateProvider > > ,
275- NoOpInspector ,
276- PrecompilesMap ,
277- > ,
278- ) -> Result < SimulationSuccessResult , BuilderTransactionError > {
279- let ResultAndState { result, state } = match evm. transact ( & signed_tx) {
305+ tx : OpTransactionRequest ,
306+ expected_logs : Vec < B256 > ,
307+ evm : & mut OpEvm < impl Database , NoOpInspector , PrecompilesMap > ,
308+ ) -> Result < SimulationSuccessResult < T > , BuilderTransactionError > {
309+ let tx_env = tx. try_into_tx_env ( evm. cfg ( ) , evm. block ( ) ) ?;
310+ let to = tx_env. base . kind . into_to ( ) . unwrap_or_default ( ) ;
311+
312+ let ResultAndState { result, state } = match evm. transact ( tx_env) {
280313 Ok ( res) => res,
281314 Err ( err) => {
282315 if err. is_invalid_tx_err ( ) {
@@ -291,28 +324,54 @@ pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Def
291324
292325 match result {
293326 ExecutionResult :: Success {
294- logs,
295- gas_used,
296327 output,
328+ gas_used,
329+ logs,
297330 ..
298331 } => {
299- if let Some ( topic) = expected_topic
300- && !logs. iter ( ) . any ( |log| log. topics ( ) . first ( ) == Some ( & topic) )
332+ let topics: HashSet < B256 > = logs
333+ . into_iter ( )
334+ . flat_map ( |log| log. topics ( ) . to_vec ( ) )
335+ . collect ( ) ;
336+ if !expected_logs
337+ . iter ( )
338+ . all ( |expected_topic| topics. contains ( expected_topic) )
301339 {
302340 return Err ( BuilderTransactionError :: InvalidContract (
303- signed_tx. to ( ) . unwrap_or_default ( ) ,
304- InvalidContractDataError :: InvalidLogs ( topic) ,
341+ to,
342+ InvalidContractDataError :: InvalidLogs (
343+ expected_logs,
344+ topics. into_iter ( ) . collect ( ) ,
345+ ) ,
305346 ) ) ;
306347 }
307- Ok ( SimulationSuccessResult {
348+ let return_output = T :: abi_decode_returns ( & output. into_data ( ) ) . map_err ( |_| {
349+ BuilderTransactionError :: InvalidContract (
350+ to,
351+ InvalidContractDataError :: OutputAbiDecodeError ,
352+ )
353+ } ) ?;
354+ Ok ( SimulationSuccessResult :: < T > {
308355 gas_used,
309- output : output . into_data ( ) ,
356+ output : return_output ,
310357 state_changes : state,
311358 } )
312359 }
313- ExecutionResult :: Revert { output, .. } => Err ( revert_handler ( output) ) ,
360+ ExecutionResult :: Revert { output, .. } => {
361+ let revert = ContractError :: < E > :: abi_decode ( & output)
362+ . and_then ( |reason| {
363+ reason
364+ . try_into ( )
365+ . map_err ( |_| Error :: Other ( Cow :: Borrowed ( "failed to convert to revert" ) ) )
366+ } )
367+ . or_else ( |_| Revert :: abi_decode ( & output) )
368+ . unwrap_or_else ( |_| {
369+ Revert :: from ( format ! ( "unknown revert: {}" , hex:: encode( & output) ) )
370+ } ) ;
371+ Err ( BuilderTransactionError :: TransactionReverted ( to, revert) )
372+ }
314373 ExecutionResult :: Halt { reason, .. } => Err ( BuilderTransactionError :: other (
315- BuilderTransactionError :: TransactionHalted ( reason) ,
374+ BuilderTransactionError :: TransactionHalted ( to , reason) ,
316375 ) ) ,
317376 }
318377 }
@@ -335,7 +394,7 @@ impl<ExtraCtx: Debug + Default> BuilderTxBase<ExtraCtx> {
335394 pub ( super ) fn simulate_builder_tx (
336395 & self ,
337396 ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
338- db : & mut State < impl Database > ,
397+ db : impl DatabaseRef ,
339398 ) -> Result < Option < BuilderTransactionCtx > , BuilderTransactionError > {
340399 match self . signer {
341400 Some ( signer) => {
@@ -380,15 +439,12 @@ impl<ExtraCtx: Debug + Default> BuilderTxBase<ExtraCtx> {
380439 fn signed_builder_tx (
381440 & self ,
382441 ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
383- db : & mut State < impl Database > ,
442+ db : impl DatabaseRef ,
384443 signer : Signer ,
385444 gas_used : u64 ,
386445 message : Vec < u8 > ,
387446 ) -> Result < Recovered < OpTransactionSigned > , BuilderTransactionError > {
388- let nonce = db
389- . load_cache_account ( signer. address )
390- . map ( |acc| acc. account_info ( ) . unwrap_or_default ( ) . nonce )
391- . map_err ( |_| BuilderTransactionError :: AccountLoadFailed ( signer. address ) ) ?;
447+ let nonce = get_nonce ( db, signer. address ) ?;
392448
393449 // Create the EIP-1559 transaction
394450 let tx = OpTypedTransaction :: Eip1559 ( TxEip1559 {
@@ -411,20 +467,17 @@ impl<ExtraCtx: Debug + Default> BuilderTxBase<ExtraCtx> {
411467 }
412468}
413469
414- pub fn get_nonce (
415- db : & mut State < impl Database > ,
416- address : Address ,
417- ) -> Result < u64 , BuilderTransactionError > {
418- db. load_cache_account ( address)
419- . map ( |acc| acc. account_info ( ) . unwrap_or_default ( ) . nonce )
470+ pub fn get_nonce ( db : impl DatabaseRef , address : Address ) -> Result < u64 , BuilderTransactionError > {
471+ db. basic_ref ( address)
472+ . map ( |acc| acc. unwrap_or_default ( ) . nonce )
420473 . map_err ( |_| BuilderTransactionError :: AccountLoadFailed ( address) )
421474}
422475
423476pub fn get_balance (
424- db : & mut State < impl Database > ,
477+ db : impl DatabaseRef ,
425478 address : Address ,
426479) -> Result < U256 , BuilderTransactionError > {
427- db. load_cache_account ( address)
428- . map ( |acc| acc. account_info ( ) . unwrap_or_default ( ) . balance )
480+ db. basic_ref ( address)
481+ . map ( |acc| acc. unwrap_or_default ( ) . balance )
429482 . map_err ( |_| BuilderTransactionError :: AccountLoadFailed ( address) )
430483}
0 commit comments