diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs
new file mode 100644
index 0000000000..4b0c052df8
--- /dev/null
+++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs
@@ -0,0 +1,205 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Parity Bridges Common.
+
+// Parity Bridges Common is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity Bridges Common is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity Bridges Common. If not, see .
+
+//! Transaction extension that rejects bridge-related transactions, that include
+//! obsolete (duplicated) data or do not pass some additional pallet-specific
+//! checks.
+
+use crate::messages_call_ext::MessagesCallSubType;
+use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType;
+use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype;
+use sp_runtime::transaction_validity::TransactionValidity;
+
+/// A duplication of the `FilterCall` trait.
+///
+/// We need this trait in order to be able to implement it for the messages pallet,
+/// since the implementation is done outside of the pallet crate.
+pub trait BridgeRuntimeFilterCall {
+ /// Checks if a runtime call is valid.
+ fn validate(call: &Call) -> TransactionValidity;
+}
+
+impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet
+where
+ T: pallet_bridge_grandpa::Config,
+ T::RuntimeCall: GrandpaCallSubType,
+{
+ fn validate(call: &T::RuntimeCall) -> TransactionValidity {
+ GrandpaCallSubType::::check_obsolete_submit_finality_proof(call)
+ }
+}
+
+impl BridgeRuntimeFilterCall
+ for pallet_bridge_parachains::Pallet
+where
+ T: pallet_bridge_parachains::Config,
+ T::RuntimeCall: ParachainsCallSubtype,
+{
+ fn validate(call: &T::RuntimeCall) -> TransactionValidity {
+ ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call)
+ }
+}
+
+impl, I: 'static> BridgeRuntimeFilterCall
+ for pallet_bridge_messages::Pallet
+where
+ T::RuntimeCall: MessagesCallSubType,
+{
+ /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation
+ /// transactions, that are delivering outdated messages/confirmations. Without this validation,
+ /// even honest relayers may lose their funds if there are multiple relays running and
+ /// submitting the same messages/confirmations.
+ fn validate(call: &T::RuntimeCall) -> TransactionValidity {
+ call.check_obsolete_call()
+ }
+}
+
+/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension.
+///
+/// ## Example
+///
+/// ```nocompile
+/// generate_bridge_reject_obsolete_headers_and_messages!{
+/// Call, AccountId
+/// BridgeRococoGrandpa, BridgeRococoMessages,
+/// BridgeRococoParachains
+/// }
+/// ```
+///
+/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged
+/// headers and messages. Without that extension, even honest relayers may lose their funds if
+/// there are multiple relays running and submitting the same information.
+#[macro_export]
+macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
+ ($call:ty, $account_id:ty, $($filter_call:ty),*) => {
+ #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)]
+ pub struct BridgeRejectObsoleteHeadersAndMessages;
+ impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages {
+ const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
+ type AccountId = $account_id;
+ type Call = $call;
+ type AdditionalSigned = ();
+ type Pre = ();
+
+ fn additional_signed(&self) -> sp_std::result::Result<
+ (),
+ sp_runtime::transaction_validity::TransactionValidityError,
+ > {
+ Ok(())
+ }
+
+ fn validate(
+ &self,
+ _who: &Self::AccountId,
+ call: &Self::Call,
+ _info: &sp_runtime::traits::DispatchInfoOf,
+ _len: usize,
+ ) -> sp_runtime::transaction_validity::TransactionValidity {
+ let valid = sp_runtime::transaction_validity::ValidTransaction::default();
+ $(
+ let valid = valid
+ .combine_with(<$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<$call>>::validate(call)?);
+ )*
+ Ok(valid)
+ }
+
+ fn pre_dispatch(
+ self,
+ who: &Self::AccountId,
+ call: &Self::Call,
+ info: &sp_runtime::traits::DispatchInfoOf,
+ len: usize,
+ ) -> Result {
+ self.validate(who, call, info, len).map(drop)
+ }
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use frame_support::{assert_err, assert_ok};
+ use sp_runtime::{
+ traits::SignedExtension,
+ transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
+ };
+
+ pub struct MockCall {
+ data: u32,
+ }
+
+ impl sp_runtime::traits::Dispatchable for MockCall {
+ type RuntimeOrigin = ();
+ type Config = ();
+ type Info = ();
+ type PostInfo = ();
+
+ fn dispatch(
+ self,
+ _origin: Self::RuntimeOrigin,
+ ) -> sp_runtime::DispatchResultWithInfo {
+ unimplemented!()
+ }
+ }
+
+ struct FirstFilterCall;
+ impl BridgeRuntimeFilterCall for FirstFilterCall {
+ fn validate(call: &MockCall) -> TransactionValidity {
+ if call.data <= 1 {
+ return InvalidTransaction::Custom(1).into()
+ }
+
+ Ok(ValidTransaction { priority: 1, ..Default::default() })
+ }
+ }
+
+ struct SecondFilterCall;
+ impl BridgeRuntimeFilterCall for SecondFilterCall {
+ fn validate(call: &MockCall) -> TransactionValidity {
+ if call.data <= 2 {
+ return InvalidTransaction::Custom(2).into()
+ }
+
+ Ok(ValidTransaction { priority: 2, ..Default::default() })
+ }
+ }
+
+ #[test]
+ fn test() {
+ generate_bridge_reject_obsolete_headers_and_messages!(
+ MockCall,
+ (),
+ FirstFilterCall,
+ SecondFilterCall
+ );
+
+ assert_err!(
+ BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0),
+ InvalidTransaction::Custom(1)
+ );
+
+ assert_err!(
+ BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0),
+ InvalidTransaction::Custom(2)
+ );
+
+ assert_ok!(
+ BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0),
+ ValidTransaction { priority: 3, ..Default::default() }
+ )
+ }
+}
diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs
new file mode 100644
index 0000000000..3f1b506aaa
--- /dev/null
+++ b/bridges/bin/runtime-common/src/extensions/mod.rs
@@ -0,0 +1,21 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Parity Bridges Common.
+
+// Parity Bridges Common is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity Bridges Common is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity Bridges Common. If not, see .
+
+//! Bridge-specific transaction extensions.
+
+pub mod check_obsolete_extension;
+pub mod priority_calculator;
+pub mod refund_relayer_extension;
diff --git a/bridges/bin/runtime-common/src/priority_calculator.rs b/bridges/bin/runtime-common/src/extensions/priority_calculator.rs
similarity index 100%
rename from bridges/bin/runtime-common/src/priority_calculator.rs
rename to bridges/bin/runtime-common/src/extensions/priority_calculator.rs
diff --git a/bridges/bin/runtime-common/src/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
similarity index 99%
rename from bridges/bin/runtime-common/src/refund_relayer_extension.rs
rename to bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
index 455392a0a2..f97b23ecaa 100644
--- a/bridges/bin/runtime-common/src/refund_relayer_extension.rs
+++ b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
@@ -520,8 +520,9 @@ where
}
// compute priority boost
- let priority_boost =
- crate::priority_calculator::compute_priority_boost::(bundled_messages);
+ let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::<
+ T::Priority,
+ >(bundled_messages);
let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost);
log::trace!(
diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs
index 2722f6f1c6..5679acd600 100644
--- a/bridges/bin/runtime-common/src/lib.rs
+++ b/bridges/bin/runtime-common/src/lib.rs
@@ -19,11 +19,7 @@
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
-use crate::messages_call_ext::MessagesCallSubType;
-use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType;
-use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype;
-use sp_runtime::transaction_validity::TransactionValidity;
-
+pub mod extensions;
pub mod messages;
pub mod messages_api;
pub mod messages_benchmarking;
@@ -31,8 +27,6 @@ pub mod messages_call_ext;
pub mod messages_generation;
pub mod messages_xcm_extension;
pub mod parachains_benchmarking;
-pub mod priority_calculator;
-pub mod refund_relayer_extension;
mod mock;
@@ -40,184 +34,3 @@ mod mock;
pub mod integrity;
const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch";
-
-/// A duplication of the `FilterCall` trait.
-///
-/// We need this trait in order to be able to implement it for the messages pallet,
-/// since the implementation is done outside of the pallet crate.
-pub trait BridgeRuntimeFilterCall {
- /// Checks if a runtime call is valid.
- fn validate(call: &Call) -> TransactionValidity;
-}
-
-impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet
-where
- T: pallet_bridge_grandpa::Config,
- T::RuntimeCall: GrandpaCallSubType,
-{
- fn validate(call: &T::RuntimeCall) -> TransactionValidity {
- GrandpaCallSubType::::check_obsolete_submit_finality_proof(call)
- }
-}
-
-impl BridgeRuntimeFilterCall
- for pallet_bridge_parachains::Pallet
-where
- T: pallet_bridge_parachains::Config,
- T::RuntimeCall: ParachainsCallSubtype,
-{
- fn validate(call: &T::RuntimeCall) -> TransactionValidity {
- ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call)
- }
-}
-
-impl, I: 'static> BridgeRuntimeFilterCall
- for pallet_bridge_messages::Pallet
-where
- T::RuntimeCall: MessagesCallSubType,
-{
- /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation
- /// transactions, that are delivering outdated messages/confirmations. Without this validation,
- /// even honest relayers may lose their funds if there are multiple relays running and
- /// submitting the same messages/confirmations.
- fn validate(call: &T::RuntimeCall) -> TransactionValidity {
- call.check_obsolete_call()
- }
-}
-
-/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension.
-///
-/// ## Example
-///
-/// ```nocompile
-/// generate_bridge_reject_obsolete_headers_and_messages!{
-/// Call, AccountId
-/// BridgeRococoGrandpa, BridgeRococoMessages,
-/// BridgeRococoParachains
-/// }
-/// ```
-///
-/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged
-/// headers and messages. Without that extension, even honest relayers may lose their funds if
-/// there are multiple relays running and submitting the same information.
-#[macro_export]
-macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
- ($call:ty, $account_id:ty, $($filter_call:ty),*) => {
- #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)]
- pub struct BridgeRejectObsoleteHeadersAndMessages;
- impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages {
- const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
- type AccountId = $account_id;
- type Call = $call;
- type AdditionalSigned = ();
- type Pre = ();
-
- fn additional_signed(&self) -> sp_std::result::Result<
- (),
- sp_runtime::transaction_validity::TransactionValidityError,
- > {
- Ok(())
- }
-
- fn validate(
- &self,
- _who: &Self::AccountId,
- call: &Self::Call,
- _info: &sp_runtime::traits::DispatchInfoOf,
- _len: usize,
- ) -> sp_runtime::transaction_validity::TransactionValidity {
- let valid = sp_runtime::transaction_validity::ValidTransaction::default();
- $(
- let valid = valid
- .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?);
- )*
- Ok(valid)
- }
-
- fn pre_dispatch(
- self,
- who: &Self::AccountId,
- call: &Self::Call,
- info: &sp_runtime::traits::DispatchInfoOf,
- len: usize,
- ) -> Result {
- self.validate(who, call, info, len).map(drop)
- }
- }
- };
-}
-
-#[cfg(test)]
-mod tests {
- use crate::BridgeRuntimeFilterCall;
- use frame_support::{assert_err, assert_ok};
- use sp_runtime::{
- traits::SignedExtension,
- transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
- };
-
- pub struct MockCall {
- data: u32,
- }
-
- impl sp_runtime::traits::Dispatchable for MockCall {
- type RuntimeOrigin = ();
- type Config = ();
- type Info = ();
- type PostInfo = ();
-
- fn dispatch(
- self,
- _origin: Self::RuntimeOrigin,
- ) -> sp_runtime::DispatchResultWithInfo {
- unimplemented!()
- }
- }
-
- struct FirstFilterCall;
- impl BridgeRuntimeFilterCall for FirstFilterCall {
- fn validate(call: &MockCall) -> TransactionValidity {
- if call.data <= 1 {
- return InvalidTransaction::Custom(1).into()
- }
-
- Ok(ValidTransaction { priority: 1, ..Default::default() })
- }
- }
-
- struct SecondFilterCall;
- impl BridgeRuntimeFilterCall for SecondFilterCall {
- fn validate(call: &MockCall) -> TransactionValidity {
- if call.data <= 2 {
- return InvalidTransaction::Custom(2).into()
- }
-
- Ok(ValidTransaction { priority: 2, ..Default::default() })
- }
- }
-
- #[test]
- fn test() {
- generate_bridge_reject_obsolete_headers_and_messages!(
- MockCall,
- (),
- FirstFilterCall,
- SecondFilterCall
- );
-
- assert_err!(
- BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0),
- InvalidTransaction::Custom(1)
- );
-
- assert_err!(
- BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0),
- InvalidTransaction::Custom(2)
- );
-
- assert_ok!(
- BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0),
- ValidTransaction { priority: 3, ..Default::default() }
- )
- }
-}
diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs
index bc2d0d127a..38fa7c2d90 100644
--- a/bridges/primitives/relayers/src/registration.rs
+++ b/bridges/primitives/relayers/src/registration.rs
@@ -21,7 +21,7 @@
//! required finality proofs). This extension boosts priority of message delivery
//! transactions, based on the number of bundled messages. So transaction with more
//! messages has larger priority than the transaction with less messages.
-//! See `bridge_runtime_common::priority_calculator` for details;
+//! See `bridge_runtime_common::extensions::priority_calculator` for details;
//!
//! This encourages relayers to include more messages to their delivery transactions.
//! At the same time, we are not verifying storage proofs before boosting
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
index 6dbf96edc2..8845f0538b 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
@@ -29,6 +29,10 @@ use crate::{
use bp_messages::LaneId;
use bp_runtime::Chain;
use bridge_runtime_common::{
+ extensions::refund_relayer_extension::{
+ ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter,
+ RefundableMessagesLane,
+ },
messages,
messages::{
source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
@@ -39,10 +43,6 @@ use bridge_runtime_common::{
SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
},
- refund_relayer_extension::{
- ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter,
- RefundableMessagesLane,
- },
};
use frame_support::{parameter_types, traits::PalletInfoAccess};
@@ -273,7 +273,7 @@ mod tests {
// Bulletin chain - it has the same (almost) runtime for Polkadot Bulletin and Rococo
// Bulletin, so we have to adhere Polkadot names here
- bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::<
+ bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::<
Runtime,
WithRococoBulletinMessagesInstance,
PriorityBoostPerMessage,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
index 5d55d7afba..e5a0007340 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
@@ -28,6 +28,10 @@ use crate::{
use bp_messages::LaneId;
use bp_runtime::Chain;
use bridge_runtime_common::{
+ extensions::refund_relayer_extension::{
+ ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter,
+ RefundableMessagesLane, RefundableParachain,
+ },
messages,
messages::{
source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
@@ -38,10 +42,6 @@ use bridge_runtime_common::{
SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
},
- refund_relayer_extension::{
- ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter,
- RefundableMessagesLane, RefundableParachain,
- },
};
use codec::Encode;
@@ -318,7 +318,7 @@ mod tests {
},
});
- bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::<
+ bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::<
Runtime,
WithBridgeHubWestendMessagesInstance,
PriorityBoostPerMessage,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
index bce722aa5f..d5da41cce2 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
@@ -25,6 +25,10 @@ use bp_messages::LaneId;
use bp_parachains::SingleParaStoredHeaderDataBuilder;
use bp_runtime::Chain;
use bridge_runtime_common::{
+ extensions::refund_relayer_extension::{
+ ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter,
+ RefundableMessagesLane, RefundableParachain,
+ },
messages,
messages::{
source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
@@ -35,10 +39,6 @@ use bridge_runtime_common::{
SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
},
- refund_relayer_extension::{
- ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter,
- RefundableMessagesLane, RefundableParachain,
- },
};
use codec::Encode;
use frame_support::{
@@ -352,7 +352,7 @@ mod tests {
},
});
- bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::<
+ bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::<
Runtime,
WithBridgeHubRococoMessagesInstance,
PriorityBoostPerMessage,