@@ -17,11 +17,11 @@ pub mod stateless_runtime;
17
17
use crate :: inherents:: is_runtime_upgraded;
18
18
use crate :: stateless_runtime:: StatelessRuntime ;
19
19
use domain_runtime_primitives:: opaque:: AccountId ;
20
- use domain_runtime_primitives:: CheckExtrinsicsValidityError ;
20
+ use domain_runtime_primitives:: { opaque , CheckExtrinsicsValidityError } ;
21
21
use parity_scale_codec:: Encode ;
22
22
use sc_client_api:: { backend, BlockBackend } ;
23
23
use sc_executor:: RuntimeVersionOf ;
24
- use sp_api:: { ApiError , ProvideRuntimeApi } ;
24
+ use sp_api:: { ApiError , ApiExt , ProvideRuntimeApi } ;
25
25
use sp_blockchain:: HeaderBackend ;
26
26
use sp_core:: traits:: { CodeExecutor , FetchRuntimeCode } ;
27
27
use sp_core:: H256 ;
@@ -50,6 +50,8 @@ type DomainBlockElements<CBlock> = (Vec<ExtrinsicFor<CBlock>>, Randomness);
50
50
enum BundleValidity < Extrinsic > {
51
51
/// A valid bundle contents.
52
52
Valid ( Vec < Extrinsic > ) ,
53
+ /// A valid bundle contents with signer of each extrinsic.
54
+ ValidWithSigner ( Vec < ( Option < opaque:: AccountId > , Extrinsic ) > ) ,
53
55
/// An invalid bundle reason.
54
56
Invalid ( InvalidBundleType ) ,
55
57
}
@@ -213,6 +215,30 @@ where
213
215
} ) )
214
216
}
215
217
218
+ /// NOTE: this is needed for compatible with Taurus
219
+ fn is_batch_api_available (
220
+ & self ,
221
+ parent_domain_hash : Block :: Hash ,
222
+ ) -> sp_blockchain:: Result < bool > {
223
+ let domain_runtime_api = self . client . runtime_api ( ) ;
224
+
225
+ let domain_core_api_version = domain_runtime_api
226
+ . api_version :: < dyn DomainCoreApi < Block > > ( parent_domain_hash) ?
227
+ . ok_or ( sp_blockchain:: Error :: Application ( Box :: from ( format ! (
228
+ "DomainCoreApi not found at {parent_domain_hash:?}"
229
+ ) ) ) ) ?;
230
+
231
+ let messenger_api_version = domain_runtime_api
232
+ . api_version :: < dyn MessengerApi < Block , NumberFor < CBlock > , CBlock :: Hash > > (
233
+ parent_domain_hash,
234
+ ) ?
235
+ . ok_or ( sp_blockchain:: Error :: Application ( Box :: from ( format ! (
236
+ "MessengerApi not found at {parent_domain_hash:?}"
237
+ ) ) ) ) ?;
238
+
239
+ Ok ( domain_core_api_version >= 2 && messenger_api_version >= 3 )
240
+ }
241
+
216
242
/// Filter out the invalid bundles first and then convert the remaining valid ones to
217
243
/// a list of extrinsics.
218
244
#[ allow( clippy:: type_complexity) ]
@@ -254,12 +280,22 @@ where
254
280
}
255
281
256
282
let extrinsic_root = bundle. extrinsics_root ( ) ;
257
- match self . check_bundle_validity (
258
- & bundle,
259
- & tx_range,
260
- ( parent_domain_hash, parent_domain_number) ,
261
- at_consensus_hash,
262
- ) ? {
283
+ let bundle_validity = if self . is_batch_api_available ( parent_domain_hash) ? {
284
+ self . batch_check_bundle_validity (
285
+ bundle,
286
+ & tx_range,
287
+ ( parent_domain_hash, parent_domain_number) ,
288
+ at_consensus_hash,
289
+ ) ?
290
+ } else {
291
+ self . check_bundle_validity (
292
+ & bundle,
293
+ & tx_range,
294
+ ( parent_domain_hash, parent_domain_number) ,
295
+ at_consensus_hash,
296
+ ) ?
297
+ } ;
298
+ match bundle_validity {
263
299
BundleValidity :: Valid ( extrinsics) => {
264
300
let extrinsics: Vec < _ > = match runtime_api
265
301
. extract_signer ( parent_domain_hash, extrinsics)
@@ -287,6 +323,24 @@ where
287
323
) ) ;
288
324
valid_extrinsics. extend ( extrinsics) ;
289
325
}
326
+ BundleValidity :: ValidWithSigner ( signer_and_extrinsics) => {
327
+ let bundle_digest: Vec < _ > = signer_and_extrinsics
328
+ . iter ( )
329
+ . map ( |( signer, tx) | {
330
+ (
331
+ signer. clone ( ) ,
332
+ ExtrinsicDigest :: new :: < LayoutV1 < HeaderHashingFor < Block :: Header > > > (
333
+ tx. encode ( ) ,
334
+ ) ,
335
+ )
336
+ } )
337
+ . collect ( ) ;
338
+ inboxed_bundles. push ( InboxedBundle :: valid (
339
+ HeaderHashingFor :: < Block :: Header > :: hash_of ( & bundle_digest) ,
340
+ extrinsic_root,
341
+ ) ) ;
342
+ valid_extrinsics. extend ( signer_and_extrinsics) ;
343
+ }
290
344
BundleValidity :: Invalid ( invalid_bundle_type) => {
291
345
inboxed_bundles
292
346
. push ( InboxedBundle :: invalid ( invalid_bundle_type, extrinsic_root) ) ;
@@ -433,6 +487,111 @@ where
433
487
434
488
Ok ( BundleValidity :: Valid ( extrinsics) )
435
489
}
490
+
491
+ fn batch_check_bundle_validity (
492
+ & self ,
493
+ bundle : OpaqueBundle < NumberFor < CBlock > , CBlock :: Hash , Block :: Header , Balance > ,
494
+ tx_range : & U256 ,
495
+ ( parent_domain_hash, parent_domain_number) : ( Block :: Hash , NumberFor < Block > ) ,
496
+ at_consensus_hash : CBlock :: Hash ,
497
+ ) -> sp_blockchain:: Result < BundleValidity < Block :: Extrinsic > > {
498
+ let bundle_vrf_hash =
499
+ U256 :: from_be_bytes ( * bundle. sealed_header . header . proof_of_election . vrf_hash ( ) ) ;
500
+ let bundle_length = bundle. extrinsics . len ( ) ;
501
+ let bundle_estimated_weight = bundle. estimated_weight ( ) ;
502
+ let mut maybe_invalid_bundle_type = None ;
503
+
504
+ let stateless_runtime_api = self . stateless_runtime_api ( parent_domain_hash) ?;
505
+ let consensus_runtime_api = self . consensus_client . runtime_api ( ) ;
506
+
507
+ // Check the validity of extrinsic inside the bundle, the goal is trying to find the first
508
+ // invalid tx and the first check it failed to pass, thus even an invalid tx that failed to
509
+ // pass a given check is found we still continue the following check for other txs that before
510
+ // it.
511
+ //
512
+ // NOTE: the checking order must follow `InvalidBundleType::checking_order`
513
+
514
+ let mut extrinsics = stateless_runtime_api. decode_extrinsics_prefix ( bundle. extrinsics ) ?;
515
+ if extrinsics. len ( ) != bundle_length {
516
+ // If the length changed meaning there is undecodable tx at index `extrinsics.len()`
517
+ maybe_invalid_bundle_type
518
+ . replace ( InvalidBundleType :: UndecodableTx ( extrinsics. len ( ) as u32 ) ) ;
519
+ }
520
+
521
+ let signers = match stateless_runtime_api. extract_signer_if_all_within_tx_range (
522
+ & extrinsics,
523
+ & bundle_vrf_hash,
524
+ tx_range,
525
+ ) ? {
526
+ Err ( index) => {
527
+ maybe_invalid_bundle_type. replace ( InvalidBundleType :: OutOfRangeTx ( index) ) ;
528
+ extrinsics. truncate ( index as usize ) ;
529
+
530
+ // This will never used since there is an invalid tx
531
+ Vec :: default ( )
532
+ }
533
+ Ok ( signers) => signers,
534
+ } ;
535
+
536
+ // Check if this extrinsic is an inherent extrinsic.
537
+ // If so, this is an invalid bundle since these extrinsics should not be included in the
538
+ // bundle. Extrinsic is always decodable due to the check above.
539
+ if let Some ( index) = stateless_runtime_api. find_first_inherent_extrinsic ( & extrinsics) ? {
540
+ maybe_invalid_bundle_type. replace ( InvalidBundleType :: InherentExtrinsic ( index) ) ;
541
+ extrinsics. truncate ( index as usize ) ;
542
+ }
543
+
544
+ let batch_xdm_mmr_proof =
545
+ stateless_runtime_api. batch_extract_native_xdm_mmr_proof ( & extrinsics) ?;
546
+ for ( index, xdm_mmr_proof) in batch_xdm_mmr_proof {
547
+ let ConsensusChainMmrLeafProof {
548
+ opaque_mmr_leaf,
549
+ proof,
550
+ ..
551
+ } = xdm_mmr_proof;
552
+
553
+ if consensus_runtime_api
554
+ . verify_proof ( at_consensus_hash, vec ! [ opaque_mmr_leaf] , proof) ?
555
+ . is_err ( )
556
+ {
557
+ maybe_invalid_bundle_type. replace ( InvalidBundleType :: InvalidXDM ( index) ) ;
558
+ extrinsics. truncate ( index as usize ) ;
559
+ break ;
560
+ }
561
+ }
562
+
563
+ // Using `check_extrinsics_and_do_pre_dispatch` instead of `check_transaction_validity`
564
+ // to maintain side-effect between tx in the storage buffer.
565
+ if let Err ( CheckExtrinsicsValidityError {
566
+ extrinsic_index, ..
567
+ } ) = self
568
+ . client
569
+ . runtime_api ( )
570
+ . check_extrinsics_and_do_pre_dispatch (
571
+ parent_domain_hash,
572
+ extrinsics. clone ( ) ,
573
+ parent_domain_number,
574
+ parent_domain_hash,
575
+ ) ?
576
+ {
577
+ maybe_invalid_bundle_type. replace ( InvalidBundleType :: IllegalTx ( extrinsic_index) ) ;
578
+ }
579
+
580
+ // If there is any invalid tx then return the error before checking the bundle weight,
581
+ // which is a check of the whole bundle and should only perform when all tx are valid.
582
+ if let Some ( invalid_bundle_type) = maybe_invalid_bundle_type {
583
+ return Ok ( BundleValidity :: Invalid ( invalid_bundle_type) ) ;
584
+ }
585
+
586
+ if bundle_estimated_weight != stateless_runtime_api. extrinsics_weight ( & extrinsics) ? {
587
+ return Ok ( BundleValidity :: Invalid (
588
+ InvalidBundleType :: InvalidBundleWeight ,
589
+ ) ) ;
590
+ }
591
+
592
+ let signer_and_extrinsics = signers. into_iter ( ) . zip ( extrinsics) . collect ( ) ;
593
+ Ok ( BundleValidity :: ValidWithSigner ( signer_and_extrinsics) )
594
+ }
436
595
}
437
596
438
597
#[ cfg( test) ]
0 commit comments