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 ;
1
11
use reth_optimism_primitives:: OpTransactionSigned ;
2
12
use 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} ;
3
17
4
- use crate :: tx_signer:: Signer ;
18
+ use crate :: {
19
+ builders:: context:: OpPayloadBuilderCtx , primitives:: reth:: ExecutionInfo , tx_signer:: Signer ,
20
+ } ;
5
21
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
+ }
10
131
}
11
132
12
133
// Scaffolding for how to construct the end of block builder transaction
@@ -17,16 +138,94 @@ pub struct StandardBuilderTx {
17
138
pub signer : Option < Signer > ,
18
139
}
19
140
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)
23
165
}
24
166
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)
27
201
}
202
+ }
28
203
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
+ }
31
230
}
32
231
}
0 commit comments