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