@@ -228,6 +228,50 @@ pub type ExtrinsicHash = H256;
228
228
/// Type alias for the tip.
229
229
pub type Tip = pallet_transaction_payment:: ChargeTransactionPayment < storage_hub_runtime:: Runtime > ;
230
230
231
+ /// Options for [`send_extrinsic`](crate::BlockchainService::send_extrinsic).
232
+ ///
233
+ /// You can safely use [`SendExtrinsicOptions::default`] to create a new instance of `SendExtrinsicOptions`.
234
+ #[ derive( Debug ) ]
235
+ pub struct SendExtrinsicOptions {
236
+ /// Tip to add to the transaction to incentivize the collator to include the transaction in a block.
237
+ tip : Tip ,
238
+ /// Optionally override the nonce to use when sending the transaction.
239
+ nonce : Option < u32 > ,
240
+ }
241
+
242
+ impl SendExtrinsicOptions {
243
+ pub fn new ( ) -> Self {
244
+ Self :: default ( )
245
+ }
246
+
247
+ pub fn with_tip ( mut self , tip : u128 ) -> Self {
248
+ self . tip = Tip :: from ( tip) ;
249
+ self
250
+ }
251
+
252
+ pub fn with_nonce ( mut self , nonce : Option < u32 > ) -> Self {
253
+ self . nonce = nonce;
254
+ self
255
+ }
256
+
257
+ pub fn tip ( & self ) -> Tip {
258
+ self . tip . clone ( )
259
+ }
260
+
261
+ pub fn nonce ( & self ) -> Option < u32 > {
262
+ self . nonce
263
+ }
264
+ }
265
+
266
+ impl Default for SendExtrinsicOptions {
267
+ fn default ( ) -> Self {
268
+ Self {
269
+ tip : Tip :: from ( 0 ) ,
270
+ nonce : None ,
271
+ }
272
+ }
273
+ }
274
+
231
275
/// A struct which defines a submit extrinsic retry strategy. This defines a simple strategy when
232
276
/// sending and extrinsic. It will retry a maximum number of times ([Self::max_retries]).
233
277
/// If the extrinsic is not included in a block within a certain time frame [`Self::timeout`] it is
@@ -252,10 +296,16 @@ pub struct RetryStrategy {
252
296
/// A higher value will make tips grow faster.
253
297
pub base_multiplier : f64 ,
254
298
/// An optional check function to determine if the extrinsic should be retried.
299
+ ///
255
300
/// If this is provided, the function will be called before each retry to determine if the
256
301
/// extrinsic should be retried or the submission should be considered failed. If this is not
257
302
/// provided, the extrinsic will be retried until [`Self::max_retries`] is reached.
258
- pub should_retry : Option < Box < dyn Fn ( ) -> Pin < Box < dyn Future < Output = bool > + Send > > + Send > > ,
303
+ ///
304
+ /// Additionally, the function will receive the [`WatchTransactionError`] as an argument, to
305
+ /// help determine if the extrinsic should be retried or not.
306
+ pub should_retry : Option <
307
+ Box < dyn Fn ( WatchTransactionError ) -> Pin < Box < dyn Future < Output = bool > + Send > > + Send > ,
308
+ > ,
259
309
}
260
310
261
311
impl RetryStrategy {
@@ -270,35 +320,76 @@ impl RetryStrategy {
270
320
}
271
321
}
272
322
323
+ /// Set the maximum number of times to retry sending the extrinsic.
273
324
pub fn with_max_retries ( mut self , max_retries : u32 ) -> Self {
274
325
self . max_retries = max_retries;
275
326
self
276
327
}
277
328
329
+ /// Set the timeout for the extrinsic.
330
+ ///
331
+ /// After this timeout, the extrinsic will be retried (if applicable) or fail.
278
332
pub fn with_timeout ( mut self , timeout : Duration ) -> Self {
279
333
self . timeout = timeout;
280
334
self
281
335
}
282
336
337
+ /// Set the maximum tip for the extrinsic.
338
+ ///
339
+ /// As the number of times the extrinsic is retried increases, the tip will increase
340
+ /// exponentially, up to this maximum tip.
283
341
pub fn with_max_tip ( mut self , max_tip : f64 ) -> Self {
284
342
self . max_tip = max_tip;
285
343
self
286
344
}
287
345
346
+ /// The base multiplier for the exponential backoff.
347
+ ///
348
+ /// A higher value will make the exponential backoff more aggressive, making the tip
349
+ /// increase quicker.
288
350
pub fn with_base_multiplier ( mut self , base_multiplier : f64 ) -> Self {
289
351
self . base_multiplier = base_multiplier;
290
352
self
291
353
}
292
354
355
+ /// Set a function to determine if the extrinsic should be retried.
356
+ ///
357
+ /// If this function is provided, it will be called before each retry to determine if the
358
+ /// extrinsic should be retried or the submission should be considered failed. If this function
359
+ /// is not provided, the extrinsic will be retried until [`Self::max_retries`] is reached.
360
+ ///
361
+ /// Additionally, the function will receive the [`WatchTransactionError`] as an argument, to
362
+ /// help determine if the extrinsic should be retried or not.
293
363
pub fn with_should_retry (
294
364
mut self ,
295
- should_retry : Option < Box < dyn Fn ( ) -> Pin < Box < dyn Future < Output = bool > + Send > > + Send > > ,
365
+ should_retry : Option <
366
+ Box < dyn Fn ( WatchTransactionError ) -> Pin < Box < dyn Future < Output = bool > + Send > > + Send > ,
367
+ > ,
296
368
) -> Self {
297
369
self . should_retry = should_retry;
298
370
self
299
371
}
300
372
373
+ /// Sets [`Self::should_retry`] to retry only if the extrinsic times out.
374
+ ///
375
+ /// This means that the extrinsic will not be sent again if, for example, it
376
+ /// is included in a block but it fails.
377
+ ///
378
+ /// See [`WatchTransactionError`] for other possible errors.
379
+ pub fn retry_only_if_timeout ( mut self ) -> Self {
380
+ self . should_retry = Some ( Box :: new ( |error| {
381
+ Box :: pin ( async move {
382
+ match error {
383
+ WatchTransactionError :: Timeout => true ,
384
+ _ => false ,
385
+ }
386
+ } )
387
+ } ) ) ;
388
+ self
389
+ }
390
+
301
391
/// Computes the tip for the given retry count.
392
+ ///
302
393
/// The formula for the tip is:
303
394
/// [`Self::max_tip`] * (([`Self::base_multiplier`] ^ (retry_count / [`Self::max_retries`]) - 1) /
304
395
/// ([`Self::base_multiplier`] - 1)).
@@ -330,6 +421,21 @@ impl Default for RetryStrategy {
330
421
}
331
422
}
332
423
424
+ #[ derive( thiserror:: Error , Debug , Clone ) ]
425
+ pub enum WatchTransactionError {
426
+ #[ error( "Timeout waiting for transaction to be included in a block" ) ]
427
+ Timeout ,
428
+ #[ error( "Transaction watcher channel closed" ) ]
429
+ WatcherChannelClosed ,
430
+ #[ error( "Transaction failed. DispatchError: {dispatch_error}, DispatchInfo: {dispatch_info}" ) ]
431
+ TransactionFailed {
432
+ dispatch_error : String ,
433
+ dispatch_info : String ,
434
+ } ,
435
+ #[ error( "Unexpected error: {0}" ) ]
436
+ Internal ( String ) ,
437
+ }
438
+
333
439
/// Minimum block information needed to register what is the current best block
334
440
/// and detect reorgs.
335
441
#[ derive( Debug , Clone , Encode , Decode , Default , Copy ) ]
@@ -480,47 +586,3 @@ impl Ord for ForestStorageSnapshotInfo {
480
586
}
481
587
}
482
588
}
483
-
484
- /// Options for [`send_extrinsic`](crate::BlockchainService::send_extrinsic).
485
- ///
486
- /// You can safely use [`SendExtrinsicOptions::default`] to create a new instance of `SendExtrinsicOptions`.
487
- #[ derive( Debug ) ]
488
- pub struct SendExtrinsicOptions {
489
- /// Tip to add to the transaction to incentivize the collator to include the transaction in a block.
490
- tip : Tip ,
491
- /// Optionally override the nonce to use when sending the transaction.
492
- nonce : Option < u32 > ,
493
- }
494
-
495
- impl SendExtrinsicOptions {
496
- pub fn new ( ) -> Self {
497
- Self :: default ( )
498
- }
499
-
500
- pub fn with_tip ( mut self , tip : u128 ) -> Self {
501
- self . tip = Tip :: from ( tip) ;
502
- self
503
- }
504
-
505
- pub fn with_nonce ( mut self , nonce : Option < u32 > ) -> Self {
506
- self . nonce = nonce;
507
- self
508
- }
509
-
510
- pub fn tip ( & self ) -> Tip {
511
- self . tip . clone ( )
512
- }
513
-
514
- pub fn nonce ( & self ) -> Option < u32 > {
515
- self . nonce
516
- }
517
- }
518
-
519
- impl Default for SendExtrinsicOptions {
520
- fn default ( ) -> Self {
521
- Self {
522
- tip : Tip :: from ( 0 ) ,
523
- nonce : None ,
524
- }
525
- }
526
- }
0 commit comments