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 , Signature , U256 } ;
5+ use alloy_rpc_types_eth:: TransactionInput ;
6+ use alloy_sol_types:: { SolCall , SolEvent , sol} ;
77use core:: fmt:: Debug ;
8- use op_alloy_consensus:: OpTypedTransaction ;
9- use op_revm:: OpHaltReason ;
8+ use op_alloy_rpc_types:: OpTransactionRequest ;
109use reth_evm:: { ConfigureEvm , precompiles:: PrecompilesMap } ;
11- use reth_optimism_primitives:: OpTransactionSigned ;
12- use reth_primitives:: Recovered ;
1310use reth_provider:: StateProvider ;
1411use reth_revm:: State ;
15- use revm:: {
16- DatabaseRef ,
17- context:: result:: { ExecutionResult , ResultAndState } ,
18- inspector:: NoOpInspector ,
19- } ;
12+ use revm:: { DatabaseRef , inspector:: NoOpInspector } ;
2013use tracing:: warn;
2114
2215use crate :: {
2316 builders:: {
2417 BuilderTransactionCtx , BuilderTransactionError , BuilderTransactions ,
25- InvalidContractDataError ,
26- builder_tx:: { BuilderTxBase , get_nonce } ,
18+ SimulationSuccessResult ,
19+ builder_tx:: BuilderTxBase ,
2720 context:: OpPayloadBuilderCtx ,
2821 flashblocks:: payload:: { FlashblocksExecutionInfo , FlashblocksExtraCtx } ,
22+ get_nonce,
2923 } ,
3024 flashtestations:: builder_tx:: FlashtestationsBuilderTx ,
3125 primitives:: reth:: ExecutionInfo ,
3731 #[ sol( rpc, abi) ]
3832 #[ derive( Debug ) ]
3933 interface IFlashblockNumber {
34+ uint256 public flashblockNumber;
35+
4036 function incrementFlashblockNumber( ) external;
4137
38+ function permitIncrementFlashblockNumber( uint256 currentFlashblockNumber, bytes memory signature) external;
39+
40+ function computeStructHash( uint256 currentFlashblockNumber) external pure returns ( bytes32) ;
41+
42+ function hashTypedDataV4( bytes32 structHash) external view returns ( bytes32) ;
43+
44+
4245 // @notice Emitted when flashblock index is incremented
4346 // @param newFlashblockIndex The new flashblock index (0-indexed within each L2 block)
4447 event FlashblockIncremented ( uint256 newFlashblockIndex) ;
5154 }
5255) ;
5356
54- #[ derive( Debug , thiserror:: Error ) ]
55- pub ( super ) enum FlashblockNumberError {
56- #[ error( "flashblocks number contract tx reverted: {0:?}" ) ]
57- Revert ( IFlashblockNumber :: IFlashblockNumberErrors ) ,
58- #[ error( "unknown revert: {0} err: {1}" ) ]
59- Unknown ( String , Error ) ,
60- #[ error( "halt: {0:?}" ) ]
61- Halt ( OpHaltReason ) ,
62- }
63-
6457// This will be the end of block transaction of a regular block
6558#[ derive( Debug , Clone ) ]
6659pub ( super ) struct FlashblocksBuilderTx {
@@ -133,94 +126,138 @@ impl BuilderTransactions<FlashblocksExtraCtx, FlashblocksExecutionInfo> for Flas
133126// This will be the end of block transaction of a regular block
134127#[ derive( Debug , Clone ) ]
135128pub ( super ) struct FlashblocksNumberBuilderTx {
136- pub signer : Option < Signer > ,
129+ pub signer : Signer ,
137130 pub flashblock_number_address : Address ,
131+ pub use_permit : bool ,
138132 pub base_builder_tx : BuilderTxBase < FlashblocksExtraCtx > ,
139133 pub flashtestations_builder_tx :
140134 Option < FlashtestationsBuilderTx < FlashblocksExtraCtx , FlashblocksExecutionInfo > > ,
141135}
142136
143137impl FlashblocksNumberBuilderTx {
144138 pub ( super ) fn new (
145- signer : Option < Signer > ,
139+ signer : Signer ,
146140 flashblock_number_address : Address ,
141+ use_permit : bool ,
147142 flashtestations_builder_tx : Option <
148143 FlashtestationsBuilderTx < FlashblocksExtraCtx , FlashblocksExecutionInfo > ,
149144 > ,
150145 ) -> Self {
151- let base_builder_tx = BuilderTxBase :: new ( signer) ;
146+ let base_builder_tx = BuilderTxBase :: new ( Some ( signer) ) ;
152147 Self {
153148 signer,
154149 flashblock_number_address,
150+ use_permit,
155151 base_builder_tx,
156152 flashtestations_builder_tx,
157153 }
158154 }
159155
160- // TODO: remove and clean up in favour of simulate_call()
161- fn estimate_flashblock_number_tx_gas (
156+ fn signed_increment_flashblocks_tx (
162157 & self ,
163158 ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
164- evm : & mut OpEvm < impl Database , NoOpInspector , PrecompilesMap > ,
165- signer : & Signer ,
166- nonce : u64 ,
167- ) -> Result < u64 , BuilderTransactionError > {
168- let tx = self . signed_flashblock_number_tx ( ctx, ctx. block_gas_limit ( ) , nonce, signer) ?;
169- let ResultAndState { result, .. } = match evm. transact ( & tx) {
170- Ok ( res) => res,
171- Err ( err) => {
172- return Err ( BuilderTransactionError :: EvmExecutionError ( Box :: new ( err) ) ) ;
173- }
159+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
160+ ) -> Result < BuilderTransactionCtx , BuilderTransactionError > {
161+ let calldata = IFlashblockNumber :: incrementFlashblockNumberCall { } ;
162+ self . increment_flashblocks_tx ( calldata, ctx, evm)
163+ }
164+
165+ fn increment_flashblocks_permit_signature (
166+ & self ,
167+ flashtestations_signer : & Signer ,
168+ current_flashblock_number : U256 ,
169+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
170+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
171+ ) -> Result < Signature , BuilderTransactionError > {
172+ let struct_hash_calldata = IFlashblockNumber :: computeStructHashCall {
173+ currentFlashblockNumber : current_flashblock_number,
174174 } ;
175+ let SimulationSuccessResult { output, .. } =
176+ self . simulate_flashblocks_readonly_call ( struct_hash_calldata, ctx, evm) ?;
177+ let typed_data_hash_calldata =
178+ IFlashblockNumber :: hashTypedDataV4Call { structHash : output } ;
179+ let SimulationSuccessResult { output, .. } =
180+ self . simulate_flashblocks_readonly_call ( typed_data_hash_calldata, ctx, evm) ?;
181+ let signature = flashtestations_signer. sign_message ( output) ?;
182+ Ok ( signature)
183+ }
175184
176- match result {
177- ExecutionResult :: Success { gas_used, logs, .. } => {
178- if logs. iter ( ) . any ( |log| {
179- log. topics ( ) . first ( )
180- == Some ( & IFlashblockNumber :: FlashblockIncremented :: SIGNATURE_HASH )
181- } ) {
182- Ok ( gas_used)
183- } else {
184- Err ( BuilderTransactionError :: InvalidContract (
185- self . flashblock_number_address ,
186- InvalidContractDataError :: InvalidLogs (
187- vec ! [ IFlashblockNumber :: FlashblockIncremented :: SIGNATURE_HASH ] ,
188- vec ! [ ] ,
189- ) ,
190- ) )
191- }
192- }
193- ExecutionResult :: Revert { output, .. } => Err ( BuilderTransactionError :: other (
194- IFlashblockNumber :: IFlashblockNumberErrors :: abi_decode ( & output)
195- . map ( FlashblockNumberError :: Revert )
196- . unwrap_or_else ( |e| FlashblockNumberError :: Unknown ( hex:: encode ( output) , e) ) ,
197- ) ) ,
198- ExecutionResult :: Halt { reason, .. } => Err ( BuilderTransactionError :: other (
199- FlashblockNumberError :: Halt ( reason) ,
200- ) ) ,
201- }
185+ fn signed_increment_flashblocks_permit_tx (
186+ & self ,
187+ flashtestations_signer : & Signer ,
188+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
189+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
190+ ) -> Result < BuilderTransactionCtx , BuilderTransactionError > {
191+ let current_flashblock_calldata = IFlashblockNumber :: flashblockNumberCall { } ;
192+ let SimulationSuccessResult { output, .. } =
193+ self . simulate_flashblocks_readonly_call ( current_flashblock_calldata, ctx, evm) ?;
194+ let signature =
195+ self . increment_flashblocks_permit_signature ( flashtestations_signer, output, ctx, evm) ?;
196+ let calldata = IFlashblockNumber :: permitIncrementFlashblockNumberCall {
197+ currentFlashblockNumber : output,
198+ signature : signature. as_bytes ( ) . into ( ) ,
199+ } ;
200+ self . increment_flashblocks_tx ( calldata, ctx, evm)
201+ }
202+
203+ fn increment_flashblocks_tx < T : SolCall + Clone > (
204+ & self ,
205+ calldata : T ,
206+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
207+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
208+ ) -> Result < BuilderTransactionCtx , BuilderTransactionError > {
209+ let SimulationSuccessResult { gas_used, .. } = self . simulate_flashblocks_call (
210+ calldata. clone ( ) ,
211+ vec ! [ IFlashblockNumber :: FlashblockIncremented :: SIGNATURE_HASH ] ,
212+ ctx,
213+ evm,
214+ ) ?;
215+ let signed_tx = self . sign_tx (
216+ self . flashblock_number_address ,
217+ self . signer ,
218+ gas_used,
219+ calldata. abi_encode ( ) . into ( ) ,
220+ ctx,
221+ evm. db_mut ( ) ,
222+ ) ?;
223+ let da_size =
224+ op_alloy_flz:: tx_estimated_size_fjord_bytes ( signed_tx. encoded_2718 ( ) . as_slice ( ) ) ;
225+ Ok ( BuilderTransactionCtx {
226+ signed_tx,
227+ gas_used,
228+ da_size,
229+ is_top_of_block : true ,
230+ } )
231+ }
232+
233+ fn simulate_flashblocks_readonly_call < T : SolCall > (
234+ & self ,
235+ calldata : T ,
236+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
237+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
238+ ) -> Result < SimulationSuccessResult < T > , BuilderTransactionError > {
239+ self . simulate_flashblocks_call ( calldata, vec ! [ ] , ctx, evm)
202240 }
203241
204- fn signed_flashblock_number_tx (
242+ fn simulate_flashblocks_call < T : SolCall > (
205243 & self ,
244+ calldata : T ,
245+ expected_logs : Vec < B256 > ,
206246 ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
207- gas_limit : u64 ,
208- nonce : u64 ,
209- signer : & Signer ,
210- ) -> Result < Recovered < OpTransactionSigned > , secp256k1:: Error > {
211- 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)
247+ evm : & mut OpEvm < impl Database + DatabaseRef , NoOpInspector , PrecompilesMap > ,
248+ ) -> Result < SimulationSuccessResult < T > , BuilderTransactionError > {
249+ let tx_req = OpTransactionRequest :: default ( )
250+ . gas_limit ( ctx. block_gas_limit ( ) )
251+ . max_fee_per_gas ( ctx. base_fee ( ) . into ( ) )
252+ . to ( self . flashblock_number_address )
253+ . from ( self . signer . address ) // use tee key as signer for simulations
254+ . nonce ( get_nonce ( evm. db ( ) , self . signer . address ) ?)
255+ . input ( TransactionInput :: new ( calldata. abi_encode ( ) . into ( ) ) ) ;
256+ self . simulate_call :: < T , IFlashblockNumber :: IFlashblockNumberErrors > (
257+ tx_req,
258+ expected_logs,
259+ evm,
260+ )
224261 }
225262}
226263
@@ -242,46 +279,35 @@ impl BuilderTransactions<FlashblocksExtraCtx, FlashblocksExecutionInfo>
242279 builder_txs. extend ( self . base_builder_tx . simulate_builder_tx ( ctx, & mut * db) ?) ;
243280 } else {
244281 // 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 ) ?;
282+ let mut evm = ctx. evm_config . evm_with_env ( & mut * db, ctx. evm_env . clone ( ) ) ;
283+ evm. modify_cfg ( |cfg| {
284+ cfg. disable_balance_check = true ;
285+ cfg. disable_block_gas_limit = true ;
286+ } ) ;
253287
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- ) ?;
288+ let flashblocks_num_tx = if let Some ( flashtestations) = & self . flashtestations_builder_tx
289+ && self . use_permit
290+ {
291+ self . signed_increment_flashblocks_permit_tx (
292+ flashtestations. tee_signer ( ) ,
293+ ctx,
294+ & mut evm,
295+ )
296+ } else {
297+ self . signed_increment_flashblocks_tx ( ctx, & mut evm)
298+ } ;
264299
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- } ;
300+ let tx = match flashblocks_num_tx {
301+ Ok ( tx) => Some ( tx) ,
302+ Err ( e) => {
303+ warn ! ( target: "builder_tx" , error = ?e, "flashblocks number contract tx simulation failed, defaulting to fallback builder tx" ) ;
304+ self . base_builder_tx
305+ . simulate_builder_tx ( ctx, & mut * db) ?
306+ . map ( |tx| tx. set_top_of_block ( ) )
307+ }
308+ } ;
282309
283- builder_txs. extend ( tx) ;
284- }
310+ builder_txs. extend ( tx) ;
285311 }
286312
287313 if ctx. is_last_flashblock ( ) {
0 commit comments