1use bp_parachains::SubmitParachainHeadsInfo;
22use bp_relayers::ExplicitOrAccountParams;
23use bp_runtime::Parachain;
24use pallet_bridge_grandpa::{
25 BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper,
26};
27use pallet_bridge_messages::CallSubType as MessagesCallSubType;
28use pallet_bridge_parachains::{CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper};
29use pallet_bridge_relayers::Pallet as RelayersPallet;
30use sp_runtime::{
31 traits::{Get, UniqueSaturatedInto},
32 transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder},
33};
34use sp_std::marker::PhantomData;
35
36#[doc(hidden)]
38pub mod __private {
39 pub use tuplex;
40}
41
42pub trait BridgeRuntimeFilterCall<AccountId, Call> {
47 type ToPostDispatch;
49 fn validate(who: &AccountId, call: &Call) -> (Self::ToPostDispatch, TransactionValidity);
53 fn post_dispatch(_who: &AccountId, _has_failed: bool, _to_post_dispatch: Self::ToPostDispatch) {
55 }
56}
57
58pub struct CheckAndBoostBridgeGrandpaTransactions<T, I, Priority, SlashAccount>(
65 PhantomData<(T, I, Priority, SlashAccount)>,
66);
67
68impl<T, I: 'static, Priority: Get<TransactionPriority>, SlashAccount: Get<T::AccountId>>
69 BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
70 for CheckAndBoostBridgeGrandpaTransactions<T, I, Priority, SlashAccount>
71where
72 T: pallet_bridge_relayers::Config + pallet_bridge_grandpa::Config<I>,
73 T::RuntimeCall: GrandpaCallSubType<T, I>,
74{
75 type ToPostDispatch = Option<BridgedBlockNumber<T, I>>;
77
78 fn validate(
79 who: &T::AccountId,
80 call: &T::RuntimeCall,
81 ) -> (Self::ToPostDispatch, TransactionValidity) {
82 match GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call) {
83 Ok(Some(our_tx)) => {
84 let to_post_dispatch = Some(our_tx.base.block_number);
85 let total_priority_boost =
86 compute_priority_boost::<T, _, Priority>(who, our_tx.improved_by);
87 (
88 to_post_dispatch,
89 ValidTransactionBuilder::default().priority(total_priority_boost).build(),
90 )
91 },
92 Ok(None) => (None, ValidTransactionBuilder::default().build()),
93 Err(e) => (None, Err(e)),
94 }
95 }
96
97 fn post_dispatch(
98 relayer: &T::AccountId,
99 has_failed: bool,
100 bundled_block_number: Self::ToPostDispatch,
101 ) {
102 let Some(bundled_block_number) = bundled_block_number else { return };
104 let has_failed =
106 has_failed || !SubmitFinalityProofHelper::<T, I>::was_successful(bundled_block_number);
107
108 if !has_failed {
109 return
110 }
111
112 RelayersPallet::<T>::slash_and_deregister(
114 relayer,
115 ExplicitOrAccountParams::Explicit(SlashAccount::get()),
116 );
117 }
118}
119
120pub struct CheckAndBoostBridgeParachainsTransactions<
127 T,
128 ParachainsInstance,
129 Para,
130 Priority,
131 SlashAccount,
132>(PhantomData<(T, ParachainsInstance, Para, Priority, SlashAccount)>);
133
134impl<
135 T,
136 ParachainsInstance,
137 Para,
138 Priority: Get<TransactionPriority>,
139 SlashAccount: Get<T::AccountId>,
140 > BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
141 for CheckAndBoostBridgeParachainsTransactions<T, ParachainsInstance, Para, Priority, SlashAccount>
142where
143 T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config<ParachainsInstance>,
144 ParachainsInstance: 'static,
145 Para: Parachain,
146 T::RuntimeCall: ParachainsCallSubtype<T, ParachainsInstance>,
147{
148 type ToPostDispatch = Option<SubmitParachainHeadsInfo>;
150
151 fn validate(
152 who: &T::AccountId,
153 call: &T::RuntimeCall,
154 ) -> (Self::ToPostDispatch, TransactionValidity) {
155 match ParachainsCallSubtype::<T, ParachainsInstance>::check_obsolete_submit_parachain_heads(
156 call,
157 ) {
158 Ok(Some(our_tx)) if our_tx.base.para_id.0 == Para::PARACHAIN_ID => {
159 let to_post_dispatch = Some(our_tx.base);
160 let total_priority_boost =
161 compute_priority_boost::<T, _, Priority>(&who, our_tx.improved_by);
162 (
163 to_post_dispatch,
164 ValidTransactionBuilder::default().priority(total_priority_boost).build(),
165 )
166 },
167 Ok(_) => (None, ValidTransactionBuilder::default().build()),
168 Err(e) => (None, Err(e)),
169 }
170 }
171
172 fn post_dispatch(relayer: &T::AccountId, has_failed: bool, maybe_update: Self::ToPostDispatch) {
173 let Some(update) = maybe_update else { return };
175 let has_failed = has_failed ||
177 !SubmitParachainHeadsHelper::<T, ParachainsInstance>::was_successful(&update);
178
179 if !has_failed {
180 return
181 }
182
183 RelayersPallet::<T>::slash_and_deregister(
185 relayer,
186 ExplicitOrAccountParams::Explicit(SlashAccount::get()),
187 );
188 }
189}
190
191impl<T, I: 'static> BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
192 for pallet_bridge_grandpa::Pallet<T, I>
193where
194 T: pallet_bridge_grandpa::Config<I>,
195 T::RuntimeCall: GrandpaCallSubType<T, I>,
196{
197 type ToPostDispatch = ();
198 fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
199 (
200 (),
201 GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call)
202 .and_then(|_| ValidTransactionBuilder::default().build()),
203 )
204 }
205}
206
207impl<T, I: 'static> BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
208 for pallet_bridge_parachains::Pallet<T, I>
209where
210 T: pallet_bridge_parachains::Config<I>,
211 T::RuntimeCall: ParachainsCallSubtype<T, I>,
212{
213 type ToPostDispatch = ();
214 fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
215 (
216 (),
217 ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call)
218 .and_then(|_| ValidTransactionBuilder::default().build()),
219 )
220 }
221}
222
223impl<T: pallet_bridge_messages::Config<I>, I: 'static>
224 BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall> for pallet_bridge_messages::Pallet<T, I>
225where
226 T::RuntimeCall: MessagesCallSubType<T, I>,
227{
228 type ToPostDispatch = ();
229 fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
234 ((), call.check_obsolete_call())
235 }
236}
237
238fn compute_priority_boost<T, N, Priority>(
240 relayer: &T::AccountId,
241 improved_by: N,
242) -> TransactionPriority
243where
244 T: pallet_bridge_relayers::Config,
245 N: UniqueSaturatedInto<TransactionPriority>,
246 Priority: Get<TransactionPriority>,
247{
248 let is_relayer_registration_active = RelayersPallet::<T>::is_registration_active(relayer);
250 let improved_by: TransactionPriority = improved_by.unique_saturated_into().saturating_sub(1);
252 let boost_per_header = if is_relayer_registration_active { Priority::get() } else { 0 };
254 improved_by.saturating_mul(boost_per_header)
255}
256
257#[macro_export]
273macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
274 ($call:ty, $account_id:ty, $($filter_call:ty),*) => {
275 #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)]
276 pub struct BridgeRejectObsoleteHeadersAndMessages;
277 impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages {
278 const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
279 type AccountId = $account_id;
280 type Call = $call;
281 type AdditionalSigned = ();
282 type Pre = (
283 $account_id,
284 ( $(
285 <$filter_call as $crate::extensions::BridgeRuntimeFilterCall<
286 $account_id,
287 $call,
288 >>::ToPostDispatch,
289 )* ),
290 );
291
292 fn additional_signed(&self) -> sp_std::result::Result<
293 (),
294 sp_runtime::transaction_validity::TransactionValidityError,
295 > {
296 Ok(())
297 }
298
299 #[allow(unused_variables)]
300 fn validate(
301 &self,
302 who: &Self::AccountId,
303 call: &Self::Call,
304 _info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
305 _len: usize,
306 ) -> sp_runtime::transaction_validity::TransactionValidity {
307 let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default();
308 let to_prepare = ();
309 $(
310 let (from_validate, call_filter_validity) = <
311 $filter_call as
312 $crate::extensions::BridgeRuntimeFilterCall<
313 Self::AccountId,
314 $call,
315 >>::validate(&who, call);
316 let tx_validity = tx_validity.combine_with(call_filter_validity?);
317 )*
318 Ok(tx_validity)
319 }
320
321 #[allow(unused_variables)]
322 fn pre_dispatch(
323 self,
324 relayer: &Self::AccountId,
325 call: &Self::Call,
326 info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
327 len: usize,
328 ) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
329 use $crate::extensions::__private::tuplex::PushBack;
330
331 let to_post_dispatch = ();
332 $(
333 let (from_validate, call_filter_validity) = <
334 $filter_call as
335 $crate::extensions::BridgeRuntimeFilterCall<
336 $account_id,
337 $call,
338 >>::validate(&relayer, call);
339 let _ = call_filter_validity?;
340 let to_post_dispatch = to_post_dispatch.push_back(from_validate);
341 )*
342 Ok((relayer.clone(), to_post_dispatch))
343 }
344
345 #[allow(unused_variables)]
346 fn post_dispatch(
347 to_post_dispatch: Option<Self::Pre>,
348 info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
349 post_info: &sp_runtime::traits::PostDispatchInfoOf<Self::Call>,
350 len: usize,
351 result: &sp_runtime::DispatchResult,
352 ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> {
353 use $crate::extensions::__private::tuplex::PopFront;
354
355 let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) };
356 let has_failed = result.is_err();
357 $(
358 let (item, to_post_dispatch) = to_post_dispatch.pop_front();
359 <
360 $filter_call as
361 $crate::extensions::BridgeRuntimeFilterCall<
362 $account_id,
363 $call,
364 >>::post_dispatch(&relayer, has_failed, item);
365 )*
366 Ok(())
367 }
368 }
369 };
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use crate::mock::*;
376 use bp_header_chain::StoredHeaderDataBuilder;
377 use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData};
378 use bp_parachains::{BestParaHeadHash, ParaInfo};
379 use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
380 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
381 use bp_runtime::HeaderId;
382 use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID};
383 use frame_support::{assert_err, assert_ok, traits::fungible::Mutate};
384 use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet};
385 use pallet_bridge_parachains::Call as ParachainsCall;
386 use sp_runtime::{
387 traits::{parameter_types, ConstU64, Header as _, SignedExtension},
388 transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
389 DispatchError,
390 };
391
392 parameter_types! {
393 pub MsgProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
394 test_lane_id(),
395 TEST_BRIDGED_CHAIN_ID,
396 RewardsAccountOwner::ThisChain,
397 );
398 pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
399 test_lane_id(),
400 TEST_BRIDGED_CHAIN_ID,
401 RewardsAccountOwner::BridgedChain,
402 );
403 }
404
405 pub struct MockCall {
406 data: u32,
407 }
408
409 impl sp_runtime::traits::Dispatchable for MockCall {
410 type RuntimeOrigin = u64;
411 type Config = ();
412 type Info = ();
413 type PostInfo = ();
414
415 fn dispatch(
416 self,
417 _origin: Self::RuntimeOrigin,
418 ) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
419 unimplemented!()
420 }
421 }
422
423 pub struct FirstFilterCall;
424 impl FirstFilterCall {
425 fn post_dispatch_called_with(success: bool) {
426 frame_support::storage::unhashed::put(&[1], &success);
427 }
428
429 fn verify_post_dispatch_called_with(success: bool) {
430 assert_eq!(frame_support::storage::unhashed::get::<bool>(&[1]), Some(success));
431 }
432 }
433
434 impl BridgeRuntimeFilterCall<u64, MockCall> for FirstFilterCall {
435 type ToPostDispatch = u64;
436 fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
437 if call.data <= 1 {
438 return (1, InvalidTransaction::Custom(1).into())
439 }
440
441 (1, Ok(ValidTransaction { priority: 1, ..Default::default() }))
442 }
443
444 fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
445 Self::post_dispatch_called_with(!has_failed);
446 assert_eq!(to_post_dispatch, 1);
447 }
448 }
449
450 pub struct SecondFilterCall;
451
452 impl SecondFilterCall {
453 fn post_dispatch_called_with(success: bool) {
454 frame_support::storage::unhashed::put(&[2], &success);
455 }
456
457 fn verify_post_dispatch_called_with(success: bool) {
458 assert_eq!(frame_support::storage::unhashed::get::<bool>(&[2]), Some(success));
459 }
460 }
461
462 impl BridgeRuntimeFilterCall<u64, MockCall> for SecondFilterCall {
463 type ToPostDispatch = u64;
464 fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
465 if call.data <= 2 {
466 return (2, InvalidTransaction::Custom(2).into())
467 }
468
469 (2, Ok(ValidTransaction { priority: 2, ..Default::default() }))
470 }
471
472 fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
473 Self::post_dispatch_called_with(!has_failed);
474 assert_eq!(to_post_dispatch, 2);
475 }
476 }
477
478 fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance {
479 let test_stake: ThisChainBalance = TestStake::get();
480 ExistentialDeposit::get().saturating_add(test_stake * 100)
481 }
482
483 fn delivery_rewards_account() -> ThisChainAccountId {
487 TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get())
488 }
489
490 fn confirmation_rewards_account() -> ThisChainAccountId {
491 TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get())
492 }
493
494 fn relayer_account_at_this_chain() -> ThisChainAccountId {
495 0
496 }
497
498 fn initialize_environment(
499 best_relay_header_number: BridgedChainBlockNumber,
500 parachain_head_at_relay_header_number: BridgedChainBlockNumber,
501 best_message: MessageNonce,
502 ) {
503 let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect();
504 let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default());
505 pallet_bridge_grandpa::CurrentAuthoritySet::<TestRuntime>::put(
506 StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(),
507 );
508 pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(best_relay_header);
509 pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
510 best_relay_header.hash(),
511 bp_test_utils::test_header::<BridgedChainHeader>(0).build(),
512 );
513
514 let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID);
515 let para_info = ParaInfo {
516 best_head_hash: BestParaHeadHash {
517 at_relay_block_number: parachain_head_at_relay_header_number,
518 head_hash: [parachain_head_at_relay_header_number as u8; 32].into(),
519 },
520 next_imported_hash_position: 0,
521 };
522 pallet_bridge_parachains::ParasInfo::<TestRuntime>::insert(para_id, para_info);
523
524 let lane_id = test_lane_id();
525 let in_lane_data =
526 InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() };
527 pallet_bridge_messages::InboundLanes::<TestRuntime>::insert(lane_id, in_lane_data);
528
529 let out_lane_data =
530 OutboundLaneData { latest_received_nonce: best_message, ..Default::default() };
531 pallet_bridge_messages::OutboundLanes::<TestRuntime>::insert(lane_id, out_lane_data);
532
533 Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap();
534 Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap();
535 Balances::mint_into(
536 &relayer_account_at_this_chain(),
537 initial_balance_of_relayer_account_at_this_chain(),
538 )
539 .unwrap();
540 }
541
542 fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall {
543 let relay_header = BridgedChainHeader::new(
544 relay_header_number,
545 Default::default(),
546 Default::default(),
547 Default::default(),
548 Default::default(),
549 );
550 let relay_justification = make_default_justification(&relay_header);
551
552 RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof {
553 finality_target: Box::new(relay_header),
554 justification: relay_justification,
555 })
556 }
557
558 fn submit_parachain_head_call(
559 parachain_head_at_relay_header_number: BridgedChainBlockNumber,
560 ) -> RuntimeCall {
561 RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads {
562 at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()),
563 parachains: vec![(
564 ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
565 [parachain_head_at_relay_header_number as u8; 32].into(),
566 )],
567 parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
568 })
569 }
570
571 #[test]
572 fn test_generated_obsolete_extension() {
573 generate_bridge_reject_obsolete_headers_and_messages!(
574 MockCall,
575 u64,
576 FirstFilterCall,
577 SecondFilterCall
578 );
579
580 run_test(|| {
581 assert_err!(
582 BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 1 }, &(), 0),
583 InvalidTransaction::Custom(1)
584 );
585 assert_err!(
586 BridgeRejectObsoleteHeadersAndMessages.pre_dispatch(
587 &42,
588 &MockCall { data: 1 },
589 &(),
590 0
591 ),
592 InvalidTransaction::Custom(1)
593 );
594
595 assert_err!(
596 BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 2 }, &(), 0),
597 InvalidTransaction::Custom(2)
598 );
599 assert_err!(
600 BridgeRejectObsoleteHeadersAndMessages.pre_dispatch(
601 &42,
602 &MockCall { data: 2 },
603 &(),
604 0
605 ),
606 InvalidTransaction::Custom(2)
607 );
608
609 assert_eq!(
610 BridgeRejectObsoleteHeadersAndMessages
611 .validate(&42, &MockCall { data: 3 }, &(), 0)
612 .unwrap(),
613 ValidTransaction { priority: 3, ..Default::default() },
614 );
615 assert_eq!(
616 BridgeRejectObsoleteHeadersAndMessages
617 .pre_dispatch(&42, &MockCall { data: 3 }, &(), 0)
618 .unwrap(),
619 (42, (1, 2)),
620 );
621
622 assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch(
625 Some((0, (1, 2))),
626 &(),
627 &(),
628 0,
629 &Ok(())
630 ));
631 FirstFilterCall::verify_post_dispatch_called_with(true);
632 SecondFilterCall::verify_post_dispatch_called_with(true);
633
634 assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch(
637 Some((0, (1, 2))),
638 &(),
639 &(),
640 0,
641 &Err(DispatchError::BadOrigin)
642 ));
643 FirstFilterCall::verify_post_dispatch_called_with(false);
644 SecondFilterCall::verify_post_dispatch_called_with(false);
645 });
646 }
647
648 frame_support::parameter_types! {
649 pub SlashDestination: ThisChainAccountId = 42;
650 }
651
652 type BridgeGrandpaWrapper =
653 CheckAndBoostBridgeGrandpaTransactions<TestRuntime, (), ConstU64<1_000>, SlashDestination>;
654
655 #[test]
656 fn grandpa_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
657 run_test(|| {
658 initialize_environment(100, 100, 100);
659
660 let priority_boost = BridgeGrandpaWrapper::validate(
661 &relayer_account_at_this_chain(),
662 &submit_relay_header_call(200),
663 )
664 .1
665 .unwrap()
666 .priority;
667 assert_eq!(priority_boost, 0);
668 })
669 }
670
671 #[test]
672 fn grandpa_wrapper_boosts_extensions_for_registered_relayer() {
673 run_test(|| {
674 initialize_environment(100, 100, 100);
675 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
676 .unwrap();
677
678 let priority_boost = BridgeGrandpaWrapper::validate(
679 &relayer_account_at_this_chain(),
680 &submit_relay_header_call(200),
681 )
682 .1
683 .unwrap()
684 .priority;
685 assert_eq!(priority_boost, 99_000);
686 })
687 }
688
689 #[test]
690 fn grandpa_wrapper_slashes_registered_relayer_if_transaction_fails() {
691 run_test(|| {
692 initialize_environment(100, 100, 100);
693 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
694 .unwrap();
695
696 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
697 BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), true, Some(150));
698 assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
699 })
700 }
701
702 #[test]
703 fn grandpa_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
704 run_test(|| {
705 initialize_environment(100, 100, 100);
706 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
707 .unwrap();
708
709 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
710 BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), false, Some(100));
711 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
712 })
713 }
714
715 type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions<
716 TestRuntime,
717 (),
718 BridgedUnderlyingParachain,
719 ConstU64<1_000>,
720 SlashDestination,
721 >;
722
723 #[test]
724 fn parachains_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
725 run_test(|| {
726 initialize_environment(100, 100, 100);
727
728 let priority_boost = BridgeParachainsWrapper::validate(
729 &relayer_account_at_this_chain(),
730 &submit_parachain_head_call(200),
731 )
732 .1
733 .unwrap()
734 .priority;
735 assert_eq!(priority_boost, 0);
736 })
737 }
738
739 #[test]
740 fn parachains_wrapper_boosts_extensions_for_registered_relayer() {
741 run_test(|| {
742 initialize_environment(100, 100, 100);
743 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
744 .unwrap();
745
746 let priority_boost = BridgeParachainsWrapper::validate(
747 &relayer_account_at_this_chain(),
748 &submit_parachain_head_call(200),
749 )
750 .1
751 .unwrap()
752 .priority;
753 assert_eq!(priority_boost, 99_000);
754 })
755 }
756
757 #[test]
758 fn parachains_wrapper_slashes_registered_relayer_if_transaction_fails() {
759 run_test(|| {
760 initialize_environment(100, 100, 100);
761 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
762 .unwrap();
763
764 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
765 BridgeParachainsWrapper::post_dispatch(
766 &relayer_account_at_this_chain(),
767 true,
768 Some(SubmitParachainHeadsInfo {
769 at_relay_block: HeaderId(150, Default::default()),
770 para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
771 para_head_hash: [150u8; 32].into(),
772 is_free_execution_expected: false,
773 }),
774 );
775 assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
776 })
777 }
778
779 #[test]
780 fn parachains_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
781 run_test(|| {
782 initialize_environment(100, 100, 100);
783 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
784 .unwrap();
785
786 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
787 BridgeParachainsWrapper::post_dispatch(
788 &relayer_account_at_this_chain(),
789 false,
790 Some(SubmitParachainHeadsInfo {
791 at_relay_block: HeaderId(100, Default::default()),
792 para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
793 para_head_hash: [100u8; 32].into(),
794 is_free_execution_expected: false,
795 }),
796 );
797 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
798 })
799 }
800}