@@ -4,7 +4,7 @@ use crate::{
4
4
utils:: extract_signature_components,
5
5
} ;
6
6
use alloy:: {
7
- consensus:: { SimpleCoder , Transaction , constants :: GWEI_TO_WEI } ,
7
+ consensus:: SimpleCoder ,
8
8
eips:: BlockNumberOrTag ,
9
9
network:: { TransactionBuilder , TransactionBuilder4844 } ,
10
10
primitives:: { FixedBytes , TxHash , U256 } ,
@@ -13,7 +13,7 @@ use alloy::{
13
13
sol_types:: { SolCall , SolError } ,
14
14
transports:: TransportError ,
15
15
} ;
16
- use eyre:: { bail, eyre } ;
16
+ use eyre:: bail;
17
17
use init4_bin_base:: deps:: {
18
18
metrics:: { counter, histogram} ,
19
19
tracing:: { self , Instrument , debug, debug_span, error, info, instrument, warn} ,
@@ -101,21 +101,11 @@ impl SubmitTask {
101
101
let data = submitCall { fills, header, v, r, s } . abi_encode ( ) ;
102
102
103
103
let sidecar = block. encode_blob :: < SimpleCoder > ( ) . build ( ) ?;
104
- Ok ( TransactionRequest :: default ( )
105
- . with_blob_sidecar ( sidecar)
106
- . with_input ( data)
107
- . with_max_priority_fee_per_gas ( ( GWEI_TO_WEI * 16 ) as u128 ) )
108
- }
109
104
110
- /// Returns the next host block height.
111
- async fn next_host_block_height ( & self ) -> eyre:: Result < u64 > {
112
- let result = self . provider ( ) . get_block_number ( ) . await ?;
113
- let next = result. checked_add ( 1 ) . ok_or_else ( || eyre ! ( "next host block height overflow" ) ) ?;
114
- debug ! ( next, "next host block height" ) ;
115
- Ok ( next)
105
+ Ok ( TransactionRequest :: default ( ) . with_blob_sidecar ( sidecar) . with_input ( data) )
116
106
}
117
107
118
- /// Prepares and then sends the EIP-4844 transaction with a sidecar encoded with a rollup block to the network.
108
+ /// Prepares and sends the EIP-4844 transaction with a sidecar encoded with a rollup block to the network.
119
109
async fn submit_transaction (
120
110
& self ,
121
111
retry_count : usize ,
@@ -158,19 +148,13 @@ impl SubmitTask {
158
148
if let Err ( TransportError :: ErrorResp ( e) ) =
159
149
self . provider ( ) . call ( tx. clone ( ) ) . block ( BlockNumberOrTag :: Pending . into ( ) ) . await
160
150
{
161
- error ! (
162
- code = e. code,
163
- message = %e. message,
164
- data = ?e. data,
165
- "error in transaction submission"
166
- ) ;
167
-
151
+ // NB: These errors are all handled the same way but are logged for debugging purposes
168
152
if e. as_revert_data ( )
169
153
. map ( |data| data. starts_with ( & IncorrectHostBlock :: SELECTOR ) )
170
154
. unwrap_or_default ( )
171
155
{
172
156
debug ! ( %e, "incorrect host block" ) ;
173
- return Some ( Ok ( ControlFlow :: Retry ) ) ;
157
+ return Some ( Ok ( ControlFlow :: Skip ) ) ;
174
158
}
175
159
176
160
if e. as_revert_data ( )
@@ -189,6 +173,12 @@ impl SubmitTask {
189
173
return Some ( Ok ( ControlFlow :: Skip ) ) ;
190
174
}
191
175
176
+ error ! (
177
+ code = e. code,
178
+ message = %e. message,
179
+ data = ?e. data,
180
+ "unknown error in host transaction simulation call"
181
+ ) ;
192
182
return Some ( Ok ( ControlFlow :: Skip ) ) ;
193
183
}
194
184
@@ -205,22 +195,14 @@ impl SubmitTask {
205
195
) -> Result < TransactionRequest , eyre:: Error > {
206
196
// TODO: ENG-1082 Implement fills
207
197
let fills = vec ! [ ] ;
208
-
209
198
// Extract the signature components from the response
210
199
let ( v, r, s) = extract_signature_components ( & resp. sig ) ;
211
200
212
- // Bump gas with each retry to replace the previous
213
- // transaction while maintaining the same nonce
214
- // TODO: Clean this up if this works
215
- let gas_coefficient: u64 = ( 15 * ( retry_count + 1 ) ) . try_into ( ) . unwrap ( ) ;
216
- let gas_limit: u64 = 1_500_000 + ( gas_coefficient * 1_000_000 ) ;
217
- let max_priority_fee_per_gas: u128 = ( retry_count as u128 ) ;
218
- debug ! (
219
- retry_count,
220
- gas_coefficient, gas_limit, max_priority_fee_per_gas, "calculated gas limit"
221
- ) ;
201
+ // Calculate gas limits based on retry attempts
202
+ let ( max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas) =
203
+ calculate_gas_limits ( retry_count) ;
222
204
223
- // manually retrieve nonce
205
+ // manually retrieve nonce // TODO: Maybe this should be done in Env task and passed through elsewhere
224
206
let nonce =
225
207
self . provider ( ) . get_transaction_count ( self . provider ( ) . default_signer_address ( ) ) . await ?;
226
208
debug ! ( nonce, "assigned nonce" ) ;
@@ -238,13 +220,12 @@ impl SubmitTask {
238
220
// Create a blob transaction with the blob header and signature values and return it
239
221
let tx = self
240
222
. build_blob_tx ( fills, header, v, r, s, block) ?
241
- . with_from ( self . provider ( ) . default_signer_address ( ) )
242
223
. with_to ( self . config . builder_helper_address )
243
- . with_gas_limit ( gas_limit )
224
+ . with_max_fee_per_gas ( max_fee_per_gas )
244
225
. with_max_priority_fee_per_gas ( max_priority_fee_per_gas)
226
+ . with_max_fee_per_blob_gas ( max_fee_per_blob_gas)
245
227
. with_nonce ( nonce) ;
246
228
247
- debug ! ( ?tx, "prepared transaction request" ) ;
248
229
Ok ( tx)
249
230
}
250
231
@@ -255,23 +236,16 @@ impl SubmitTask {
255
236
resp : & SignResponse ,
256
237
tx : TransactionRequest ,
257
238
) -> Result < ControlFlow , eyre:: Error > {
258
- debug ! (
259
- host_block_number = %resp. req. host_block_number,
260
- gas_limit = %resp. req. gas_limit,
261
- nonce = ?tx. nonce,
262
- "sending transaction to network"
263
- ) ;
264
-
265
239
// assign the nonce and fill the rest of the values
266
240
let SendableTx :: Envelope ( tx) = self . provider ( ) . fill ( tx) . await ? else {
267
241
bail ! ( "failed to fill transaction" )
268
242
} ;
269
- debug ! ( tx_hash = %tx . tx_hash ( ) , nonce = ?tx. nonce ( ) , gas_limit = ?tx . gas_limit ( ) , blob_gas_used = ?tx . blob_gas_used ( ) , "filled blob transaction" ) ;
243
+ debug ! ( tx_hash = ?tx. hash ( ) , host_block_number = %resp . req . host_block_number , "sending transaction to network " ) ;
270
244
271
245
// send the tx via the primary host_provider
272
246
let fut = spawn_provider_send ! ( self . provider( ) , & tx) ;
273
247
274
- // spawn send_tx futures for all additional broadcast host_providers
248
+ // spawn send_tx futures on retry attempts for all additional broadcast host_providers
275
249
for host_provider in self . config . connect_additional_broadcast ( ) {
276
250
spawn_provider_send ! ( & host_provider, & tx) ;
277
251
}
@@ -281,16 +255,19 @@ impl SubmitTask {
281
255
error ! ( "receipts task gone" ) ;
282
256
}
283
257
284
- // question mark unwraps join error, which would be an internal panic
285
- // then if let checks for rpc error
286
258
if let Err ( e) = fut. await ? {
287
- error ! ( error = %e, "Primary tx broadcast failed. Skipping transaction." ) ;
259
+ // Detect and handle transaction underprice errors
260
+ if matches ! ( e, TransportError :: ErrorResp ( ref err) if err. code == -32000 && err. message. contains( "replacement transaction underpriced" ) )
261
+ {
262
+ debug ! ( ?tx, "underpriced transaction error - retrying tx with gas bump" ) ;
263
+ return Ok ( ControlFlow :: Retry ) ;
264
+ }
265
+
266
+ // Unknown error, log and skip
267
+ error ! ( error = %e, "Primary tx broadcast failed" ) ;
288
268
return Ok ( ControlFlow :: Skip ) ;
289
269
}
290
270
291
- // Okay so the code gets all the way to this log
292
- // but we don't see the tx hash in the logs or in the explorer,
293
- // not even as a failed TX, just not at all.
294
271
info ! (
295
272
tx_hash = %tx. tx_hash( ) ,
296
273
ru_chain_id = %resp. req. ru_chain_id,
@@ -339,54 +316,38 @@ impl SubmitTask {
339
316
340
317
// Retry loop
341
318
let result = loop {
319
+ // Log the retry attempt
342
320
let span = debug_span ! ( "SubmitTask::retrying_handle_inbound" , retries) ;
343
321
344
- let inbound_result = match self
345
- . handle_inbound ( retries, block)
346
- . instrument ( span. clone ( ) )
347
- . await
348
- {
349
- Ok ( control_flow) => {
350
- debug ! ( ?control_flow, retries, "successfully handled inbound block" ) ;
351
- control_flow
352
- }
353
- Err ( err) => {
354
- // Log the retry attempt
355
- retries += 1 ;
356
-
357
- // Delay until next slot if we get a 403 error
358
- if err. to_string ( ) . contains ( "403 Forbidden" ) {
359
- let ( slot_number, _, _) = self . calculate_slot_window ( ) ?;
360
- debug ! ( slot_number, ?block, "403 detected - not assigned to slot" ) ;
361
- return Ok ( ControlFlow :: Skip ) ;
362
- } else {
363
- error ! ( error = %err, "error handling inbound block" ) ;
322
+ let inbound_result =
323
+ match self . handle_inbound ( retries, block) . instrument ( span. clone ( ) ) . await {
324
+ Ok ( control_flow) => control_flow,
325
+ Err ( err) => {
326
+ // Delay until next slot if we get a 403 error
327
+ if err. to_string ( ) . contains ( "403 Forbidden" ) {
328
+ let ( slot_number, _, _) = self . calculate_slot_window ( ) ?;
329
+ debug ! ( slot_number, "403 detected - skipping slot" ) ;
330
+ return Ok ( ControlFlow :: Skip ) ;
331
+ } else {
332
+ error ! ( error = %err, "error handling inbound block" ) ;
333
+ }
334
+
335
+ ControlFlow :: Retry
364
336
}
365
-
366
- ControlFlow :: Retry
367
- }
368
- } ;
337
+ } ;
369
338
370
339
let guard = span. entered ( ) ;
371
340
372
341
match inbound_result {
373
342
ControlFlow :: Retry => {
343
+ retries += 1 ;
374
344
if retries > retry_limit {
375
345
counter ! ( "builder.building_too_many_retries" ) . increment ( 1 ) ;
376
346
debug ! ( "retries exceeded - skipping block" ) ;
377
347
return Ok ( ControlFlow :: Skip ) ;
378
348
}
379
349
drop ( guard) ;
380
-
381
- // Detect a slot change and break out of the loop in that case too
382
- let ( this_slot, start, end) = self . calculate_slot_window ( ) ?;
383
- if this_slot != current_slot {
384
- debug ! ( "slot changed - skipping block" ) ;
385
- break inbound_result;
386
- }
387
-
388
- // Otherwise retry the block
389
- debug ! ( retries, this_slot, start, end, "retrying block" ) ;
350
+ debug ! ( retries, start, end, "retrying block" ) ;
390
351
continue ;
391
352
}
392
353
ControlFlow :: Skip => {
@@ -423,6 +384,12 @@ impl SubmitTask {
423
384
now. duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( )
424
385
}
425
386
387
+ /// Returns the next host block height.
388
+ async fn next_host_block_height ( & self ) -> eyre:: Result < u64 > {
389
+ let block_num = self . provider ( ) . get_block_number ( ) . await ?;
390
+ Ok ( block_num + 1 )
391
+ }
392
+
426
393
/// Task future for the submit task
427
394
/// NB: This task assumes that the simulator will only send it blocks for
428
395
/// slots that it's assigned.
@@ -469,3 +436,27 @@ impl SubmitTask {
469
436
( sender, handle)
470
437
}
471
438
}
439
+
440
+ fn calculate_gas_limits ( retry_count : usize ) -> ( u128 , u128 , u128 ) {
441
+ let base_fee_per_gas: u128 = 100_000_000_000 ;
442
+ let base_priority_fee_per_gas: u128 = 2_000_000_000 ;
443
+ let base_fee_per_blob_gas: u128 = 1_000_000_000 ;
444
+
445
+ let bump_multiplier = 1150u128 . pow ( retry_count as u32 ) ; // 15% bump
446
+ let blob_bump_multiplier = 2000u128 . pow ( retry_count as u32 ) ; // 100% bump (double each time) for blob gas
447
+ let bump_divisor = 1000u128 . pow ( retry_count as u32 ) ;
448
+
449
+ let max_fee_per_gas = base_fee_per_gas * bump_multiplier / bump_divisor;
450
+ let max_priority_fee_per_gas = base_priority_fee_per_gas * bump_multiplier / bump_divisor;
451
+ let max_fee_per_blob_gas = base_fee_per_blob_gas * blob_bump_multiplier / bump_divisor;
452
+
453
+ debug ! (
454
+ retry_count,
455
+ max_fee_per_gas,
456
+ max_priority_fee_per_gas,
457
+ max_fee_per_blob_gas,
458
+ "calculated bumped gas parameters"
459
+ ) ;
460
+
461
+ ( max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas)
462
+ }
0 commit comments