1- use alloy_consensus:: TxEip1559 ;
21use alloy_eips:: Encodable2718 ;
32use alloy_evm:: { Database , Evm } ;
43use alloy_op_evm:: OpEvm ;
5- use alloy_primitives:: { Address , TxKind } ;
6- use alloy_sol_types:: { Error , SolCall , SolEvent , SolInterface , sol} ;
4+ use alloy_primitives:: { Address , B256 , Bytes , Signature , U256 } ;
5+ use alloy_sol_types:: { SolCall , SolEvent , SolInterface , SolValue , sol} ;
76use core:: fmt:: Debug ;
8- use op_alloy_consensus:: OpTypedTransaction ;
9- use op_revm:: OpHaltReason ;
107use reth_evm:: { ConfigureEvm , precompiles:: PrecompilesMap } ;
11- use reth_optimism_primitives:: OpTransactionSigned ;
12- use reth_primitives:: Recovered ;
138use reth_provider:: StateProvider ;
14- use reth_revm:: State ;
15- use revm:: {
16- DatabaseRef ,
17- context:: result:: { ExecutionResult , ResultAndState } ,
18- inspector:: NoOpInspector ,
19- } ;
9+ use reth_revm:: { State , database:: StateProviderDatabase } ;
10+ use revm:: inspector:: NoOpInspector ;
2011use tracing:: warn;
2112
2213use crate :: {
2314 builders:: {
2415 BuilderTransactionCtx , BuilderTransactionError , BuilderTransactions ,
25- InvalidContractDataError ,
26- builder_tx:: { BuilderTxBase , get_nonce } ,
16+ InvalidContractDataError , SimulationSuccessResult ,
17+ builder_tx:: BuilderTxBase ,
2718 context:: OpPayloadBuilderCtx ,
2819 flashblocks:: payload:: { FlashblocksExecutionInfo , FlashblocksExtraCtx } ,
2920 } ,
3728 #[ sol( rpc, abi) ]
3829 #[ derive( Debug ) ]
3930 interface IFlashblockNumber {
31+ uint256 public flashblockNumber;
32+
4033 function incrementFlashblockNumber( ) external;
4134
35+ function permitIncrementFlashblockNumber( uint256 currentFlashblockNumber, bytes memory signature) external;
36+
37+ function computeStructHash( uint256 currentFlashblockNumber) external pure returns ( bytes32) ;
38+
39+ function hashTypedDataV4( bytes32 structHash) external view returns ( bytes32) ;
40+
41+
4242 // @notice Emitted when flashblock index is incremented
4343 // @param newFlashblockIndex The new flashblock index (0-indexed within each L2 block)
4444 event FlashblockIncremented ( uint256 newFlashblockIndex) ;
5555pub ( super ) enum FlashblockNumberError {
5656 #[ error( "flashblocks number contract tx reverted: {0:?}" ) ]
5757 Revert ( IFlashblockNumber :: IFlashblockNumberErrors ) ,
58- #[ error( "unknown revert: {0} err: {1}" ) ]
59- Unknown ( String , Error ) ,
60- #[ error( "halt: {0:?}" ) ]
61- Halt ( OpHaltReason ) ,
58+ #[ error( "unknown revert: {0}" ) ]
59+ Unknown ( String ) ,
6260}
6361
6462// This will be the end of block transaction of a regular block
@@ -133,25 +131,28 @@ impl BuilderTransactions<FlashblocksExtraCtx, FlashblocksExecutionInfo> for Flas
133131// This will be the end of block transaction of a regular block
134132#[ derive( Debug , Clone ) ]
135133pub ( super ) struct FlashblocksNumberBuilderTx {
136- pub signer : Option < Signer > ,
134+ pub signer : Signer ,
137135 pub flashblock_number_address : Address ,
136+ pub use_permit : bool ,
138137 pub base_builder_tx : BuilderTxBase < FlashblocksExtraCtx > ,
139138 pub flashtestations_builder_tx :
140139 Option < FlashtestationsBuilderTx < FlashblocksExtraCtx , FlashblocksExecutionInfo > > ,
141140}
142141
143142impl FlashblocksNumberBuilderTx {
144143 pub ( super ) fn new (
145- signer : Option < Signer > ,
144+ signer : Signer ,
146145 flashblock_number_address : Address ,
146+ use_permit : bool ,
147147 flashtestations_builder_tx : Option <
148148 FlashtestationsBuilderTx < FlashblocksExtraCtx , FlashblocksExecutionInfo > ,
149149 > ,
150150 ) -> Self {
151- let base_builder_tx = BuilderTxBase :: new ( signer) ;
151+ let base_builder_tx = BuilderTxBase :: new ( Some ( signer) ) ;
152152 Self {
153153 signer,
154154 flashblock_number_address,
155+ use_permit,
155156 base_builder_tx,
156157 flashtestations_builder_tx,
157158 }
@@ -200,27 +201,173 @@ impl FlashblocksNumberBuilderTx {
200201 ) ) ,
201202 }
202203 }
204+
205+ fn current_flashblock_number (
206+ & self ,
207+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
208+ evm : & mut OpEvm <
209+ & mut State < StateProviderDatabase < impl StateProvider > > ,
210+ NoOpInspector ,
211+ PrecompilesMap ,
212+ > ,
213+ ) -> Result < U256 , BuilderTransactionError > {
214+ let current_flashblock_calldata = IFlashblockNumber :: flashblockNumberCall { } . abi_encode ( ) ;
215+ let SimulationSuccessResult { output, .. } =
216+ self . simulate_flashblocks_call ( current_flashblock_calldata, None , ctx, evm) ?;
217+ IFlashblockNumber :: flashblockNumberCall:: abi_decode_returns ( & output) . map_err ( |_| {
218+ BuilderTransactionError :: InvalidContract (
219+ self . flashblock_number_address ,
220+ InvalidContractDataError :: OutputAbiDecodeError ,
221+ )
222+ } )
223+ }
203224
204- fn signed_flashblock_number_tx (
225+ fn signed_increment_flashblocks_tx (
205226 & self ,
206227 ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
207- gas_limit : u64 ,
208- nonce : u64 ,
209- signer : & Signer ,
210- ) -> Result < Recovered < OpTransactionSigned > , secp256k1:: Error > {
228+ evm : & mut OpEvm <
229+ & mut State < StateProviderDatabase < impl StateProvider > > ,
230+ NoOpInspector ,
231+ PrecompilesMap ,
232+ > ,
233+ ) -> Result < BuilderTransactionCtx , BuilderTransactionError > {
211234 let calldata = IFlashblockNumber :: incrementFlashblockNumberCall { } . abi_encode ( ) ;
212- // Create the EIP-1559 transaction
213- let tx = OpTypedTransaction :: Eip1559 ( TxEip1559 {
214- chain_id : ctx. chain_id ( ) ,
215- nonce,
216- gas_limit,
217- max_fee_per_gas : ctx. base_fee ( ) . into ( ) ,
218- max_priority_fee_per_gas : 0 ,
219- to : TxKind :: Call ( self . flashblock_number_address ) ,
220- input : calldata. into ( ) ,
221- ..Default :: default ( )
222- } ) ;
223- signer. sign_tx ( tx)
235+ let SimulationSuccessResult { gas_used, .. } = self . simulate_flashblocks_call (
236+ calldata. clone ( ) ,
237+ Some ( IFlashblockNumber :: FlashblockIncremented :: SIGNATURE_HASH ) ,
238+ ctx,
239+ evm,
240+ ) ?;
241+ let signed_tx = self . sign_tx (
242+ self . flashblock_number_address ,
243+ self . signer ,
244+ Some ( gas_used) ,
245+ calldata. into ( ) ,
246+ ctx,
247+ evm. db_mut ( ) ,
248+ ) ?;
249+ let da_size =
250+ op_alloy_flz:: tx_estimated_size_fjord_bytes ( signed_tx. encoded_2718 ( ) . as_slice ( ) ) ;
251+ Ok ( BuilderTransactionCtx {
252+ signed_tx,
253+ gas_used,
254+ da_size,
255+ is_top_of_block : true ,
256+ } )
257+ }
258+
259+ fn increment_flashblocks_permit_signature (
260+ & self ,
261+ flashtestations_signer : & Signer ,
262+ current_flashblock_number : U256 ,
263+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
264+ evm : & mut OpEvm <
265+ & mut State < StateProviderDatabase < impl StateProvider > > ,
266+ NoOpInspector ,
267+ PrecompilesMap ,
268+ > ,
269+ ) -> Result < Signature , BuilderTransactionError > {
270+ let struct_hash_calldata = IFlashblockNumber :: computeStructHashCall {
271+ currentFlashblockNumber : current_flashblock_number,
272+ }
273+ . abi_encode ( ) ;
274+ let SimulationSuccessResult { output, .. } =
275+ self . simulate_flashblocks_call ( struct_hash_calldata, None , ctx, evm) ?;
276+ let struct_hash = B256 :: abi_decode ( & output) . map_err ( |_| {
277+ BuilderTransactionError :: InvalidContract (
278+ self . flashblock_number_address ,
279+ InvalidContractDataError :: OutputAbiDecodeError ,
280+ )
281+ } ) ?;
282+ let typed_data_hash_calldata = IFlashblockNumber :: hashTypedDataV4Call {
283+ structHash : struct_hash,
284+ }
285+ . abi_encode ( ) ;
286+ let SimulationSuccessResult { output, .. } =
287+ self . simulate_flashblocks_call ( typed_data_hash_calldata, None , ctx, evm) ?;
288+ let typed_data_hash = B256 :: abi_decode ( & output) . map_err ( |_| {
289+ BuilderTransactionError :: InvalidContract (
290+ self . flashblock_number_address ,
291+ InvalidContractDataError :: OutputAbiDecodeError ,
292+ )
293+ } ) ?;
294+ let signature = flashtestations_signer. sign_message ( typed_data_hash) ?;
295+ Ok ( signature)
296+ }
297+
298+ fn signed_increment_flashblocks_permit_tx (
299+ & self ,
300+ flashtestations_signer : & Signer ,
301+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
302+ evm : & mut OpEvm <
303+ & mut State < StateProviderDatabase < impl StateProvider > > ,
304+ NoOpInspector ,
305+ PrecompilesMap ,
306+ > ,
307+ ) -> Result < BuilderTransactionCtx , BuilderTransactionError > {
308+ let current_flashblock_number = self . current_flashblock_number ( ctx, evm) ?;
309+ let signature = self . increment_flashblocks_permit_signature (
310+ flashtestations_signer,
311+ current_flashblock_number,
312+ ctx,
313+ evm,
314+ ) ?;
315+ let calldata = IFlashblockNumber :: permitIncrementFlashblockNumberCall {
316+ currentFlashblockNumber : current_flashblock_number,
317+ signature : signature. as_bytes ( ) . into ( ) ,
318+ }
319+ . abi_encode ( ) ;
320+ let SimulationSuccessResult { gas_used, .. } = self . simulate_flashblocks_call (
321+ calldata. clone ( ) ,
322+ Some ( IFlashblockNumber :: FlashblockIncremented :: SIGNATURE_HASH ) ,
323+ ctx,
324+ evm,
325+ ) ?;
326+ let signed_tx = self . sign_tx (
327+ self . flashblock_number_address ,
328+ self . signer ,
329+ Some ( gas_used) ,
330+ calldata. into ( ) ,
331+ ctx,
332+ evm. db_mut ( ) ,
333+ ) ?;
334+ let da_size =
335+ op_alloy_flz:: tx_estimated_size_fjord_bytes ( signed_tx. encoded_2718 ( ) . as_slice ( ) ) ;
336+ Ok ( BuilderTransactionCtx {
337+ signed_tx,
338+ gas_used,
339+ da_size,
340+ is_top_of_block : true ,
341+ } )
342+ }
343+
344+ fn simulate_flashblocks_call (
345+ & self ,
346+ calldata : Vec < u8 > ,
347+ expected_topic : Option < B256 > ,
348+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
349+ evm : & mut OpEvm <
350+ & mut State < StateProviderDatabase < impl StateProvider > > ,
351+ NoOpInspector ,
352+ PrecompilesMap ,
353+ > ,
354+ ) -> Result < SimulationSuccessResult , BuilderTransactionError > {
355+ let signed_tx = self . sign_tx (
356+ self . flashblock_number_address ,
357+ self . signer ,
358+ None ,
359+ calldata. into ( ) ,
360+ ctx,
361+ evm. db_mut ( ) ,
362+ ) ?;
363+ self . simulate_call ( signed_tx, expected_topic, Self :: handle_revert, evm)
364+ }
365+
366+ fn handle_revert ( revert_output : Bytes ) -> BuilderTransactionError {
367+ let revert_reason = IFlashblockNumber :: IFlashblockNumberErrors :: abi_decode ( & revert_output)
368+ . map ( FlashblockNumberError :: Revert )
369+ . unwrap_or_else ( |_| FlashblockNumberError :: Unknown ( hex:: encode ( revert_output) ) ) ;
370+ BuilderTransactionError :: TransactionReverted ( Box :: new ( revert_reason) )
224371 }
225372}
226373
@@ -242,46 +389,37 @@ impl BuilderTransactions<FlashblocksExtraCtx, FlashblocksExecutionInfo>
242389 builder_txs. extend ( self . base_builder_tx . simulate_builder_tx ( ctx, & mut * db) ?) ;
243390 } else {
244391 // we increment the flashblock number for the next flashblock so we don't increment in the last flashblock
245- if let Some ( signer) = & self . signer {
246- let mut evm = ctx. evm_config . evm_with_env ( & mut * db, ctx. evm_env . clone ( ) ) ;
247- evm. modify_cfg ( |cfg| {
248- cfg. disable_balance_check = true ;
249- cfg. disable_block_gas_limit = true ;
250- } ) ;
251-
252- let nonce = get_nonce ( evm. db_mut ( ) , signer. address ) ?;
253-
254- let tx = match self . estimate_flashblock_number_tx_gas ( ctx, & mut evm, signer, nonce)
255- {
256- Ok ( gas_used) => {
257- // Due to EIP-150, 63/64 of available gas is forwarded to external calls so need to add a buffer
258- let signed_tx = self . signed_flashblock_number_tx (
259- ctx,
260- gas_used * 64 / 63 ,
261- nonce,
262- signer,
263- ) ?;
264-
265- let da_size = op_alloy_flz:: tx_estimated_size_fjord_bytes (
266- signed_tx. encoded_2718 ( ) . as_slice ( ) ,
267- ) ;
268- Some ( BuilderTransactionCtx {
269- gas_used,
270- da_size,
271- signed_tx,
272- is_top_of_block : true , // number tx at top of flashblock
273- } )
274- }
275- Err ( e) => {
276- warn ! ( target: "builder_tx" , error = ?e, "Flashblocks number contract tx simulation failed, defaulting to fallback builder tx" ) ;
277- self . base_builder_tx
278- . simulate_builder_tx ( ctx, & mut * db) ?
279- . map ( |tx| tx. set_top_of_block ( ) )
280- }
281- } ;
392+ let mut evm = ctx
393+ . evm_config
394+ . evm_with_env ( & mut simulation_state, ctx. evm_env . clone ( ) ) ;
395+ evm. modify_cfg ( |cfg| {
396+ cfg. disable_balance_check = true ;
397+ cfg. disable_block_gas_limit = true ;
398+ } ) ;
282399
283- builder_txs. extend ( tx) ;
284- }
400+ let flashblocks_num_tx = if let Some ( flashtestations) = & self . flashtestations_builder_tx
401+ && self . use_permit
402+ {
403+ self . signed_increment_flashblocks_permit_tx (
404+ flashtestations. tee_signer ( ) ,
405+ ctx,
406+ & mut evm,
407+ )
408+ } else {
409+ self . signed_increment_flashblocks_tx ( ctx, & mut evm)
410+ } ;
411+
412+ let tx = match flashblocks_num_tx {
413+ Ok ( tx) => Some ( tx) ,
414+ Err ( e) => {
415+ warn ! ( target: "builder_tx" , error = ?e, "flashblocks number contract tx simulation failed, defaulting to fallback builder tx" ) ;
416+ self . base_builder_tx
417+ . simulate_builder_tx ( ctx, db) ?
418+ . map ( |tx| tx. set_top_of_block ( ) )
419+ }
420+ } ;
421+
422+ builder_txs. extend ( tx) ;
285423 }
286424
287425 if ctx. is_last_flashblock ( ) {
0 commit comments