1+ use alloy_consensus:: TxEip1559 ;
2+ use alloy_eips:: { eip7623:: TOTAL_COST_FLOOR_PER_TOKEN , Encodable2718 } ;
3+ use alloy_primitives:: {
4+ map:: foldhash:: { HashSet , HashSetExt } ,
5+ Address , TxKind ,
6+ } ;
7+ use core:: fmt:: Debug ;
8+ use op_alloy_consensus:: OpTypedTransaction ;
9+ use reth_evm:: { eth:: receipt_builder:: ReceiptBuilderCtx , ConfigureEvm , Evm } ;
10+ use reth_node_api:: PayloadBuilderError ;
111use reth_optimism_primitives:: OpTransactionSigned ;
212use reth_primitives:: Recovered ;
13+ use reth_provider:: ProviderError ;
14+ use reth_revm:: State ;
15+ use revm:: { context:: result:: ResultAndState , Database , DatabaseCommit } ;
16+ use tracing:: { debug, warn} ;
317
4- use crate :: tx_signer:: Signer ;
18+ use crate :: {
19+ builders:: context:: OpPayloadBuilderCtx , primitives:: reth:: ExecutionInfo , tx_signer:: Signer ,
20+ } ;
521
6- pub trait BuilderTx {
7- fn estimated_builder_tx_gas ( & self ) -> u64 ;
8- fn estimated_builder_tx_da_size ( & self ) -> Option < u64 > ;
9- fn signed_builder_tx ( & self ) -> Result < Recovered < OpTransactionSigned > , secp256k1:: Error > ;
22+ pub struct BuilderTransactionCtx {
23+ pub gas_used : u64 ,
24+ pub da_size : u64 ,
25+ pub signed_tx : Recovered < OpTransactionSigned > ,
26+ }
27+
28+ /// Possible error variants during construction of builder txs.
29+ #[ derive( Debug , thiserror:: Error ) ]
30+ pub enum BuilderTransactionError {
31+ /// Thrown when builder account load fails to get builder nonce
32+ #[ error( "failed to load account {0}" ) ]
33+ AccountLoadFailed ( Address ) ,
34+ /// Thrown when signature signing fails
35+ #[ error( "failed to sign transaction: {0}" ) ]
36+ SigningError ( secp256k1:: Error ) ,
37+ /// Unrecoverable error during evm execution.
38+ #[ error( "evm execution error {0}" ) ]
39+ EvmExecutionError ( Box < dyn core:: error:: Error + Send + Sync > ) ,
40+ /// Any other builder transaction errors.
41+ #[ error( transparent) ]
42+ Other ( Box < dyn core:: error:: Error + Send + Sync > ) ,
43+ }
44+
45+ impl From < secp256k1:: Error > for BuilderTransactionError {
46+ fn from ( error : secp256k1:: Error ) -> Self {
47+ BuilderTransactionError :: SigningError ( error)
48+ }
49+ }
50+
51+ impl From < BuilderTransactionError > for PayloadBuilderError {
52+ fn from ( error : BuilderTransactionError ) -> Self {
53+ match error {
54+ BuilderTransactionError :: EvmExecutionError ( e) => {
55+ PayloadBuilderError :: EvmExecutionError ( e)
56+ }
57+ _ => PayloadBuilderError :: Other ( Box :: new ( error) ) ,
58+ }
59+ }
60+ }
61+
62+ pub trait BuilderTransactions {
63+ fn simulate_builder_txs < DB , Extra : Debug + Default > (
64+ & self ,
65+ info : & mut ExecutionInfo < Extra > ,
66+ ctx : & OpPayloadBuilderCtx ,
67+ db : & mut State < DB > ,
68+ ) -> Result < Vec < BuilderTransactionCtx > , BuilderTransactionError >
69+ where
70+ DB : Database < Error = ProviderError > ;
71+
72+ fn add_builder_txs < DB , Extra : Debug + Default > (
73+ & self ,
74+ info : & mut ExecutionInfo < Extra > ,
75+ builder_ctx : & OpPayloadBuilderCtx ,
76+ db : & mut State < DB > ,
77+ ) -> Result < ( ) , BuilderTransactionError >
78+ where
79+ DB : Database < Error = ProviderError > ,
80+ {
81+ {
82+ let mut evm = builder_ctx
83+ . evm_config
84+ . evm_with_env ( & mut * db, builder_ctx. evm_env . clone ( ) ) ;
85+ let mut invalid: HashSet < Address > = HashSet :: new ( ) ;
86+ let builder_txs = self . simulate_builder_txs ( info, builder_ctx, evm. db_mut ( ) ) ?;
87+ for builder_tx in builder_txs {
88+ if invalid. contains ( & builder_tx. signed_tx . signer ( ) ) {
89+ debug ! ( target: "payload_builder" , tx_hash = ?builder_tx. signed_tx. tx_hash( ) , "builder signer invalid as previous builder tx reverted" ) ;
90+ continue ;
91+ }
92+
93+ let ResultAndState { result, state } = evm
94+ . transact ( & builder_tx. signed_tx )
95+ . map_err ( |err| BuilderTransactionError :: EvmExecutionError ( Box :: new ( err) ) ) ?;
96+
97+ if !result. is_success ( ) {
98+ warn ! ( target: "payload_builder" , tx_hash = ?builder_tx. signed_tx. tx_hash( ) , "builder tx reverted" ) ;
99+ invalid. insert ( builder_tx. signed_tx . signer ( ) ) ;
100+ continue ;
101+ }
102+
103+ // Add gas used by the transaction to cumulative gas used, before creating the receipt
104+ let gas_used = result. gas_used ( ) ;
105+ info. cumulative_gas_used += gas_used;
106+
107+ let ctx = ReceiptBuilderCtx {
108+ tx : builder_tx. signed_tx . inner ( ) ,
109+ evm : & evm,
110+ result,
111+ state : & state,
112+ cumulative_gas_used : info. cumulative_gas_used ,
113+ } ;
114+ info. receipts . push ( builder_ctx. build_receipt ( ctx, None ) ) ;
115+
116+ // Commit changes
117+ evm. db_mut ( ) . commit ( state) ;
118+
119+ // Append sender and transaction to the respective lists
120+ info. executed_senders . push ( builder_tx. signed_tx . signer ( ) ) ;
121+ info. executed_transactions
122+ . push ( builder_tx. signed_tx . into_inner ( ) ) ;
123+ }
124+
125+ // Release the db reference by dropping evm
126+ drop ( evm) ;
127+
128+ Ok ( ( ) )
129+ }
130+ }
10131}
11132
12133// Scaffolding for how to construct the end of block builder transaction
@@ -17,16 +138,94 @@ pub struct StandardBuilderTx {
17138 pub signer : Option < Signer > ,
18139}
19140
20- impl BuilderTx for StandardBuilderTx {
21- fn estimated_builder_tx_gas ( & self ) -> u64 {
22- todo ! ( )
141+ impl StandardBuilderTx {
142+ pub fn new ( signer : Option < Signer > ) -> Self {
143+ Self { signer }
144+ }
145+
146+ fn estimate_builder_tx_gas ( & self , input : & [ u8 ] ) -> u64 {
147+ // Count zero and non-zero bytes
148+ let ( zero_bytes, nonzero_bytes) = input. iter ( ) . fold ( ( 0 , 0 ) , |( zeros, nonzeros) , & byte| {
149+ if byte == 0 {
150+ ( zeros + 1 , nonzeros)
151+ } else {
152+ ( zeros, nonzeros + 1 )
153+ }
154+ } ) ;
155+
156+ // Calculate gas cost (4 gas per zero byte, 16 gas per non-zero byte)
157+ let zero_cost = zero_bytes * 4 ;
158+ let nonzero_cost = nonzero_bytes * 16 ;
159+
160+ // Tx gas should be not less than floor gas https://eips.ethereum.org/EIPS/eip-7623
161+ let tokens_in_calldata = zero_bytes + nonzero_bytes * 4 ;
162+ let floor_gas = 21_000 + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN ;
163+
164+ std:: cmp:: max ( zero_cost + nonzero_cost + 21_000 , floor_gas)
23165 }
24166
25- fn estimated_builder_tx_da_size ( & self ) -> Option < u64 > {
26- todo ! ( )
167+ fn signed_builder_tx < DB > (
168+ & self ,
169+ ctx : & OpPayloadBuilderCtx ,
170+ db : & mut State < DB > ,
171+ signer : Signer ,
172+ gas_used : u64 ,
173+ message : Vec < u8 > ,
174+ ) -> Result < Recovered < OpTransactionSigned > , BuilderTransactionError >
175+ where
176+ DB : Database < Error = ProviderError > ,
177+ {
178+ let nonce = db
179+ . load_cache_account ( signer. address )
180+ . map ( |acc| acc. account_info ( ) . unwrap_or_default ( ) . nonce )
181+ . map_err ( |_| BuilderTransactionError :: AccountLoadFailed ( signer. address ) ) ?;
182+
183+ // Create the EIP-1559 transaction
184+ let tx = OpTypedTransaction :: Eip1559 ( TxEip1559 {
185+ chain_id : ctx. chain_id ( ) ,
186+ nonce,
187+ gas_limit : gas_used,
188+ max_fee_per_gas : ctx. base_fee ( ) . into ( ) ,
189+ max_priority_fee_per_gas : 0 ,
190+ to : TxKind :: Call ( Address :: ZERO ) ,
191+ // Include the message as part of the transaction data
192+ input : message. into ( ) ,
193+ ..Default :: default ( )
194+ } ) ;
195+ // Sign the transaction
196+ let builder_tx = signer
197+ . sign_tx ( tx)
198+ . map_err ( BuilderTransactionError :: SigningError ) ?;
199+
200+ Ok ( builder_tx)
27201 }
202+ }
28203
29- fn signed_builder_tx ( & self ) -> Result < Recovered < OpTransactionSigned > , secp256k1:: Error > {
30- todo ! ( )
204+ impl BuilderTransactions for StandardBuilderTx {
205+ fn simulate_builder_txs < DB , Extra : Debug + Default > (
206+ & self ,
207+ _info : & mut ExecutionInfo < Extra > ,
208+ ctx : & OpPayloadBuilderCtx ,
209+ db : & mut State < DB > ,
210+ ) -> Result < Vec < BuilderTransactionCtx > , BuilderTransactionError >
211+ where
212+ DB : Database < Error = ProviderError > ,
213+ {
214+ match self . signer {
215+ Some ( signer) => {
216+ let message: Vec < u8 > = format ! ( "Block Number: {}" , ctx. block_number( ) ) . into_bytes ( ) ;
217+ let gas_used = self . estimate_builder_tx_gas ( & message) ;
218+ let signed_tx = self . signed_builder_tx ( ctx, db, signer, gas_used, message) ?;
219+ let da_size = op_alloy_flz:: tx_estimated_size_fjord_bytes (
220+ signed_tx. encoded_2718 ( ) . as_slice ( ) ,
221+ ) ;
222+ Ok ( vec ! [ BuilderTransactionCtx {
223+ gas_used,
224+ da_size,
225+ signed_tx,
226+ } ] )
227+ }
228+ None => Ok ( vec ! [ ] ) ,
229+ }
31230 }
32231}
0 commit comments