@@ -17,11 +17,11 @@ pub mod stateless_runtime;
1717use crate :: inherents:: is_runtime_upgraded;
1818use crate :: stateless_runtime:: StatelessRuntime ;
1919use domain_runtime_primitives:: opaque:: AccountId ;
20- use domain_runtime_primitives:: CheckExtrinsicsValidityError ;
20+ use domain_runtime_primitives:: { opaque , CheckExtrinsicsValidityError } ;
2121use parity_scale_codec:: Encode ;
2222use sc_client_api:: { backend, BlockBackend } ;
2323use sc_executor:: RuntimeVersionOf ;
24- use sp_api:: { ApiError , ProvideRuntimeApi } ;
24+ use sp_api:: { ApiError , ApiExt , ProvideRuntimeApi } ;
2525use sp_blockchain:: HeaderBackend ;
2626use sp_core:: traits:: { CodeExecutor , FetchRuntimeCode } ;
2727use sp_core:: H256 ;
@@ -50,6 +50,8 @@ type DomainBlockElements<CBlock> = (Vec<ExtrinsicFor<CBlock>>, Randomness);
5050enum BundleValidity < Extrinsic > {
5151 /// A valid bundle contents.
5252 Valid ( Vec < Extrinsic > ) ,
53+ /// A valid bundle contents with signer of each extrinsic.
54+ ValidWithSigner ( Vec < ( Option < opaque:: AccountId > , Extrinsic ) > ) ,
5355 /// An invalid bundle reason.
5456 Invalid ( InvalidBundleType ) ,
5557}
@@ -213,6 +215,30 @@ where
213215 } ) )
214216 }
215217
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+
216242 /// Filter out the invalid bundles first and then convert the remaining valid ones to
217243 /// a list of extrinsics.
218244 #[ allow( clippy:: type_complexity) ]
@@ -254,12 +280,22 @@ where
254280 }
255281
256282 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 {
263299 BundleValidity :: Valid ( extrinsics) => {
264300 let extrinsics: Vec < _ > = match runtime_api
265301 . extract_signer ( parent_domain_hash, extrinsics)
@@ -287,6 +323,24 @@ where
287323 ) ) ;
288324 valid_extrinsics. extend ( extrinsics) ;
289325 }
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+ }
290344 BundleValidity :: Invalid ( invalid_bundle_type) => {
291345 inboxed_bundles
292346 . push ( InboxedBundle :: invalid ( invalid_bundle_type, extrinsic_root) ) ;
@@ -433,6 +487,111 @@ where
433487
434488 Ok ( BundleValidity :: Valid ( extrinsics) )
435489 }
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+ }
436595}
437596
438597#[ cfg( test) ]
0 commit comments