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 #[allow(dead_code)]
275 #[derive(Clone, codec::Decode, codec::DecodeWithMemTracking, Default, codec::Encode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
276 pub struct BridgeRejectObsoleteHeadersAndMessages;
277 impl sp_runtime::traits::TransactionExtension<$call> for BridgeRejectObsoleteHeadersAndMessages {
278 const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
279 type Implicit = ();
280 type Val = Option<(
281 $account_id,
282 ( $(
283 <$filter_call as $crate::extensions::BridgeRuntimeFilterCall<
284 $account_id,
285 $call,
286 >>::ToPostDispatch,
287 )* ),
288 )>;
289 type Pre = Self::Val;
290
291 fn weight(&self, _: &$call) -> frame_support::pallet_prelude::Weight {
292 frame_support::pallet_prelude::Weight::zero()
293 }
294
295 fn validate(
296 &self,
297 origin: <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
298 call: &$call,
299 _info: &sp_runtime::traits::DispatchInfoOf<$call>,
300 _len: usize,
301 _self_implicit: Self::Implicit,
302 _inherited_implication: &impl codec::Encode,
303 _source: sp_runtime::transaction_validity::TransactionSource,
304 ) -> Result<
305 (
306 sp_runtime::transaction_validity::ValidTransaction,
307 Self::Val,
308 <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
309 ), sp_runtime::transaction_validity::TransactionValidityError
310 > {
311 use $crate::extensions::__private::tuplex::PushBack;
312 use sp_runtime::traits::AsSystemOriginSigner;
313
314 let Some(who) = origin.as_system_origin_signer() else {
315 return Ok((Default::default(), None, origin));
316 };
317
318 let to_post_dispatch = ();
319 let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default();
320 $(
321 let (from_validate, call_filter_validity) = <
322 $filter_call as
323 $crate::extensions::BridgeRuntimeFilterCall<
324 $account_id,
325 $call,
326 >>::validate(who, call);
327 let to_post_dispatch = to_post_dispatch.push_back(from_validate);
328 let tx_validity = tx_validity.combine_with(call_filter_validity?);
329 )*
330 Ok((tx_validity, Some((who.clone(), to_post_dispatch)), origin))
331 }
332
333 fn prepare(
334 self,
335 val: Self::Val,
336 _origin: &<$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
337 _call: &$call,
338 _info: &sp_runtime::traits::DispatchInfoOf<$call>,
339 _len: usize,
340 ) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
341 Ok(val)
342 }
343
344 #[allow(unused_variables)]
345 fn post_dispatch_details(
346 to_post_dispatch: Self::Pre,
347 info: &sp_runtime::traits::DispatchInfoOf<$call>,
348 post_info: &sp_runtime::traits::PostDispatchInfoOf<$call>,
349 len: usize,
350 result: &sp_runtime::DispatchResult,
351 ) -> Result<frame_support::pallet_prelude::Weight, sp_runtime::transaction_validity::TransactionValidityError> {
352 use $crate::extensions::__private::tuplex::PopFront;
353
354 let Some((relayer, to_post_dispatch)) = to_post_dispatch else {
355 return Ok(frame_support::pallet_prelude::Weight::zero())
356 };
357
358 let has_failed = result.is_err();
359 $(
360 let (item, to_post_dispatch) = to_post_dispatch.pop_front();
361 <
362 $filter_call as
363 $crate::extensions::BridgeRuntimeFilterCall<
364 $account_id,
365 $call,
366 >>::post_dispatch(&relayer, has_failed, item);
367 )*
368 Ok(frame_support::pallet_prelude::Weight::zero())
369 }
370 }
371 };
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use crate::mock::*;
378 use bp_header_chain::StoredHeaderDataBuilder;
379 use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData};
380 use bp_parachains::{BestParaHeadHash, ParaInfo};
381 use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
382 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
383 use bp_runtime::HeaderId;
384 use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID};
385 use codec::{Decode, Encode, MaxEncodedLen};
386 use frame_support::{assert_err, assert_ok, traits::fungible::Mutate};
387 use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet};
388 use pallet_bridge_parachains::Call as ParachainsCall;
389 use scale_info::TypeInfo;
390 use sp_runtime::{
391 traits::{
392 parameter_types, AsSystemOriginSigner, AsTransactionAuthorizedOrigin, ConstU64,
393 DispatchTransaction, Header as _, TransactionExtension,
394 },
395 transaction_validity::{
396 InvalidTransaction, TransactionSource::External, TransactionValidity, ValidTransaction,
397 },
398 DispatchError,
399 };
400
401 parameter_types! {
402 pub MsgProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
403 test_lane_id(),
404 TEST_BRIDGED_CHAIN_ID,
405 RewardsAccountOwner::ThisChain,
406 );
407 pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
408 test_lane_id(),
409 TEST_BRIDGED_CHAIN_ID,
410 RewardsAccountOwner::BridgedChain,
411 );
412 }
413
414 #[derive(Debug, Clone, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen)]
415 pub struct MockCall {
416 data: u32,
417 }
418
419 #[derive(Debug, Clone, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen)]
420 pub struct MockOrigin(pub u64);
421
422 impl AsSystemOriginSigner<u64> for MockOrigin {
423 fn as_system_origin_signer(&self) -> Option<&u64> {
424 Some(&self.0)
425 }
426 }
427
428 impl AsTransactionAuthorizedOrigin for MockOrigin {
429 fn is_transaction_authorized(&self) -> bool {
430 true
431 }
432 }
433
434 impl From<u64> for MockOrigin {
435 fn from(o: u64) -> Self {
436 Self(o)
437 }
438 }
439
440 impl sp_runtime::traits::Dispatchable for MockCall {
441 type RuntimeOrigin = MockOrigin;
442 type Config = ();
443 type Info = ();
444 type PostInfo = ();
445
446 fn dispatch(
447 self,
448 _origin: Self::RuntimeOrigin,
449 ) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
450 unimplemented!()
451 }
452 }
453
454 pub struct FirstFilterCall;
455 impl FirstFilterCall {
456 fn post_dispatch_called_with(success: bool) {
457 frame_support::storage::unhashed::put(&[1], &success);
458 }
459
460 fn verify_post_dispatch_called_with(success: bool) {
461 assert_eq!(frame_support::storage::unhashed::get::<bool>(&[1]), Some(success));
462 }
463 }
464
465 impl BridgeRuntimeFilterCall<u64, MockCall> for FirstFilterCall {
466 type ToPostDispatch = u64;
467 fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
468 if call.data <= 1 {
469 return (1, InvalidTransaction::Custom(1).into());
470 }
471
472 (1, Ok(ValidTransaction { priority: 1, ..Default::default() }))
473 }
474
475 fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
476 Self::post_dispatch_called_with(!has_failed);
477 assert_eq!(to_post_dispatch, 1);
478 }
479 }
480
481 pub struct SecondFilterCall;
482
483 impl SecondFilterCall {
484 fn post_dispatch_called_with(success: bool) {
485 frame_support::storage::unhashed::put(&[2], &success);
486 }
487
488 fn verify_post_dispatch_called_with(success: bool) {
489 assert_eq!(frame_support::storage::unhashed::get::<bool>(&[2]), Some(success));
490 }
491 }
492
493 impl BridgeRuntimeFilterCall<u64, MockCall> for SecondFilterCall {
494 type ToPostDispatch = u64;
495 fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
496 if call.data <= 2 {
497 return (2, InvalidTransaction::Custom(2).into());
498 }
499
500 (2, Ok(ValidTransaction { priority: 2, ..Default::default() }))
501 }
502
503 fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
504 Self::post_dispatch_called_with(!has_failed);
505 assert_eq!(to_post_dispatch, 2);
506 }
507 }
508
509 fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance {
510 let test_stake: ThisChainBalance = TestStake::get();
511 ExistentialDeposit::get().saturating_add(test_stake * 100)
512 }
513
514 fn delivery_rewards_account() -> ThisChainAccountId {
518 TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get())
519 }
520
521 fn confirmation_rewards_account() -> ThisChainAccountId {
522 TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get())
523 }
524
525 fn relayer_account_at_this_chain() -> ThisChainAccountId {
526 0
527 }
528
529 fn initialize_environment(
530 best_relay_header_number: BridgedChainBlockNumber,
531 parachain_head_at_relay_header_number: BridgedChainBlockNumber,
532 best_message: MessageNonce,
533 ) {
534 let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect();
535 let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default());
536 pallet_bridge_grandpa::CurrentAuthoritySet::<TestRuntime>::put(
537 StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(),
538 );
539 pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(best_relay_header);
540 pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
541 best_relay_header.hash(),
542 bp_test_utils::test_header::<BridgedChainHeader>(0).build(),
543 );
544
545 let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID);
546 let para_info = ParaInfo {
547 best_head_hash: BestParaHeadHash {
548 at_relay_block_number: parachain_head_at_relay_header_number,
549 head_hash: [parachain_head_at_relay_header_number as u8; 32].into(),
550 },
551 next_imported_hash_position: 0,
552 };
553 pallet_bridge_parachains::ParasInfo::<TestRuntime>::insert(para_id, para_info);
554
555 let lane_id = test_lane_id();
556 let in_lane_data =
557 InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() };
558 pallet_bridge_messages::InboundLanes::<TestRuntime>::insert(lane_id, in_lane_data);
559
560 let out_lane_data =
561 OutboundLaneData { latest_received_nonce: best_message, ..Default::default() };
562 pallet_bridge_messages::OutboundLanes::<TestRuntime>::insert(lane_id, out_lane_data);
563
564 Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap();
565 Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap();
566 Balances::mint_into(
567 &relayer_account_at_this_chain(),
568 initial_balance_of_relayer_account_at_this_chain(),
569 )
570 .unwrap();
571 }
572
573 fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall {
574 let relay_header = BridgedChainHeader::new(
575 relay_header_number,
576 Default::default(),
577 Default::default(),
578 Default::default(),
579 Default::default(),
580 );
581 let relay_justification = make_default_justification(&relay_header);
582
583 RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof {
584 finality_target: Box::new(relay_header),
585 justification: relay_justification,
586 })
587 }
588
589 fn submit_parachain_head_call(
590 parachain_head_at_relay_header_number: BridgedChainBlockNumber,
591 ) -> RuntimeCall {
592 RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads {
593 at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()),
594 parachains: vec![(
595 ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
596 [parachain_head_at_relay_header_number as u8; 32].into(),
597 )],
598 parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
599 })
600 }
601
602 #[test]
603 fn test_generated_obsolete_extension() {
604 generate_bridge_reject_obsolete_headers_and_messages!(
605 MockCall,
606 u64,
607 FirstFilterCall,
608 SecondFilterCall
609 );
610
611 run_test(|| {
612 assert_err!(
613 BridgeRejectObsoleteHeadersAndMessages.validate_only(
614 42u64.into(),
615 &MockCall { data: 1 },
616 &(),
617 0,
618 External,
619 0,
620 ),
621 InvalidTransaction::Custom(1)
622 );
623 assert_err!(
624 BridgeRejectObsoleteHeadersAndMessages.validate_and_prepare(
625 42u64.into(),
626 &MockCall { data: 1 },
627 &(),
628 0,
629 0,
630 ),
631 InvalidTransaction::Custom(1)
632 );
633
634 assert_err!(
635 BridgeRejectObsoleteHeadersAndMessages.validate_only(
636 42u64.into(),
637 &MockCall { data: 2 },
638 &(),
639 0,
640 External,
641 0,
642 ),
643 InvalidTransaction::Custom(2)
644 );
645 assert_err!(
646 BridgeRejectObsoleteHeadersAndMessages.validate_and_prepare(
647 42u64.into(),
648 &MockCall { data: 2 },
649 &(),
650 0,
651 0,
652 ),
653 InvalidTransaction::Custom(2)
654 );
655
656 assert_eq!(
657 BridgeRejectObsoleteHeadersAndMessages
658 .validate_only(42u64.into(), &MockCall { data: 3 }, &(), 0, External, 0)
659 .unwrap()
660 .0,
661 ValidTransaction { priority: 3, ..Default::default() },
662 );
663 assert_eq!(
664 BridgeRejectObsoleteHeadersAndMessages
665 .validate_and_prepare(42u64.into(), &MockCall { data: 3 }, &(), 0, 0)
666 .unwrap()
667 .0
668 .unwrap(),
669 (42, (1, 2)),
670 );
671
672 assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch_details(
675 Some((0, (1, 2))),
676 &(),
677 &(),
678 0,
679 &Ok(()),
680 ));
681 FirstFilterCall::verify_post_dispatch_called_with(true);
682 SecondFilterCall::verify_post_dispatch_called_with(true);
683
684 assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch_details(
687 Some((0, (1, 2))),
688 &(),
689 &(),
690 0,
691 &Err(DispatchError::BadOrigin),
692 ));
693 FirstFilterCall::verify_post_dispatch_called_with(false);
694 SecondFilterCall::verify_post_dispatch_called_with(false);
695 });
696 }
697
698 frame_support::parameter_types! {
699 pub SlashDestination: ThisChainAccountId = 42;
700 }
701
702 type BridgeGrandpaWrapper =
703 CheckAndBoostBridgeGrandpaTransactions<TestRuntime, (), ConstU64<1_000>, SlashDestination>;
704
705 #[test]
706 fn grandpa_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
707 run_test(|| {
708 initialize_environment(100, 100, 100);
709
710 let priority_boost = BridgeGrandpaWrapper::validate(
711 &relayer_account_at_this_chain(),
712 &submit_relay_header_call(200),
713 )
714 .1
715 .unwrap()
716 .priority;
717 assert_eq!(priority_boost, 0);
718 })
719 }
720
721 #[test]
722 fn grandpa_wrapper_boosts_extensions_for_registered_relayer() {
723 run_test(|| {
724 initialize_environment(100, 100, 100);
725 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
726 .unwrap();
727
728 let priority_boost = BridgeGrandpaWrapper::validate(
729 &relayer_account_at_this_chain(),
730 &submit_relay_header_call(200),
731 )
732 .1
733 .unwrap()
734 .priority;
735 assert_eq!(priority_boost, 99_000);
736 })
737 }
738
739 #[test]
740 fn grandpa_wrapper_slashes_registered_relayer_if_transaction_fails() {
741 run_test(|| {
742 initialize_environment(100, 100, 100);
743 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
744 .unwrap();
745
746 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
747 BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), true, Some(150));
748 assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
749 })
750 }
751
752 #[test]
753 fn grandpa_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
754 run_test(|| {
755 initialize_environment(100, 100, 100);
756 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
757 .unwrap();
758
759 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
760 BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), false, Some(100));
761 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
762 })
763 }
764
765 type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions<
766 TestRuntime,
767 (),
768 BridgedUnderlyingParachain,
769 ConstU64<1_000>,
770 SlashDestination,
771 >;
772
773 #[test]
774 fn parachains_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
775 run_test(|| {
776 initialize_environment(100, 100, 100);
777
778 let priority_boost = BridgeParachainsWrapper::validate(
779 &relayer_account_at_this_chain(),
780 &submit_parachain_head_call(200),
781 )
782 .1
783 .unwrap()
784 .priority;
785 assert_eq!(priority_boost, 0);
786 })
787 }
788
789 #[test]
790 fn parachains_wrapper_boosts_extensions_for_registered_relayer() {
791 run_test(|| {
792 initialize_environment(100, 100, 100);
793 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
794 .unwrap();
795
796 let priority_boost = BridgeParachainsWrapper::validate(
797 &relayer_account_at_this_chain(),
798 &submit_parachain_head_call(200),
799 )
800 .1
801 .unwrap()
802 .priority;
803 assert_eq!(priority_boost, 99_000);
804 })
805 }
806
807 #[test]
808 fn parachains_wrapper_slashes_registered_relayer_if_transaction_fails() {
809 run_test(|| {
810 initialize_environment(100, 100, 100);
811 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
812 .unwrap();
813
814 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
815 BridgeParachainsWrapper::post_dispatch(
816 &relayer_account_at_this_chain(),
817 true,
818 Some(SubmitParachainHeadsInfo {
819 at_relay_block: HeaderId(150, Default::default()),
820 para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
821 para_head_hash: [150u8; 32].into(),
822 is_free_execution_expected: false,
823 }),
824 );
825 assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
826 })
827 }
828
829 #[test]
830 fn parachains_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
831 run_test(|| {
832 initialize_environment(100, 100, 100);
833 BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
834 .unwrap();
835
836 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
837 BridgeParachainsWrapper::post_dispatch(
838 &relayer_account_at_this_chain(),
839 false,
840 Some(SubmitParachainHeadsInfo {
841 at_relay_block: HeaderId(100, Default::default()),
842 para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
843 para_head_hash: [100u8; 32].into(),
844 is_free_execution_expected: false,
845 }),
846 );
847 assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
848 })
849 }
850}