Skip to content

Commit

Permalink
move transaction extensions to a separate folder
Browse files Browse the repository at this point in the history
  • Loading branch information
svyatonik committed Mar 12, 2024
1 parent 824b8b9 commit e901f46
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 201 deletions.
218 changes: 218 additions & 0 deletions bin/runtime-common/src/extensions/check_obsolete_extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// 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 <http://www.gnu.org/licenses/>.

//! 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<Call> {
/// Checks if a runtime call is valid.
fn validate(call: &Call) -> TransactionValidity;
}

impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I>
where
T: pallet_bridge_grandpa::Config<I>,
T::RuntimeCall: GrandpaCallSubType<T, I>,
{
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call)
}
}

impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
for pallet_bridge_parachains::Pallet<T, I>
where
T: pallet_bridge_parachains::Config<I>,
T::RuntimeCall: ParachainsCallSubtype<T, I>,
{
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call)
}
}

impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
for pallet_bridge_messages::Pallet<T, I>
where
T::RuntimeCall: MessagesCallSubType<T, I>,
{
/// 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::TransactionExtensionBase for BridgeRejectObsoleteHeadersAndMessages {
const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
type Implicit = ();
}
impl<Context> sp_runtime::traits::TransactionExtension<$call, Context> for BridgeRejectObsoleteHeadersAndMessages {
type Pre = ();
type Val = ();

fn validate(
&self,
origin: <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
call: &$call,
_info: &sp_runtime::traits::DispatchInfoOf<$call>,
_len: usize,
_context: &mut Context,
_self_implicit: Self::Implicit,
_inherited_implication: &impl codec::Encode,
) -> Result<
(
sp_runtime::transaction_validity::ValidTransaction,
Self::Val,
<$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
), sp_runtime::transaction_validity::TransactionValidityError
> {
let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default();
$(
let call_filter_validity = <
$filter_call as
$crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<$call>
>::validate(call)?;
let tx_validity = tx_validity.combine_with(call_filter_validity);
)*
Ok((tx_validity, (), origin))
}

fn prepare(
self,
_val: Self::Val,
_origin: &<$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin,
_call: &$call,
_info: &sp_runtime::traits::DispatchInfoOf<$call>,
_len: usize,
_context: &Context,
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
Ok(())
}
}
};
}

#[cfg(test)]
mod tests {
use crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall;
use codec::Encode;
use frame_support::assert_err;
use sp_runtime::{
traits::DispatchTransaction,
transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
};

#[derive(Encode)]
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<Self::PostInfo> {
unimplemented!()
}
}

struct FirstFilterCall;
impl BridgeRuntimeFilterCall<MockCall> 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<MockCall> 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_only((), &MockCall { data: 1 }, &(), 0),
InvalidTransaction::Custom(1)
);

assert_err!(
BridgeRejectObsoleteHeadersAndMessages.validate_only((), &MockCall { data: 2 }, &(), 0),
InvalidTransaction::Custom(2)
);

assert_eq!(
BridgeRejectObsoleteHeadersAndMessages
.validate_only((), &MockCall { data: 3 }, &(), 0)
.unwrap()
.0,
ValidTransaction { priority: 3, ..Default::default() }
)
}
}
21 changes: 21 additions & 0 deletions bin/runtime-common/src/extensions/mod.rs
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

//! Bridge-specific transaction extensions.

pub mod check_obsolete_extension;
pub mod priority_calculator;
pub mod refund_relayer_extension;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.

//! Signed extension that refunds relayer if he has delivered some new messages.
//! Transaction extension that refunds relayer if he has delivered some new messages.
//! It also refunds transaction cost if the transaction is an `utility.batchAll()`
//! with calls that are: delivering new messsage and all necessary underlying headers
//! (parachain or relay chain).
Expand Down Expand Up @@ -537,8 +537,9 @@ where
}

// compute priority boost
let priority_boost =
crate::priority_calculator::compute_priority_boost::<T::Priority>(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!(
Expand Down Expand Up @@ -1023,6 +1024,7 @@ mod tests {
finality_target: Box::new(relay_header),
justification: relay_justification,
current_set_id: TEST_GRANDPA_SET_ID,
is_free_execution_expected: false,
})
}

Expand Down Expand Up @@ -1209,6 +1211,7 @@ mod tests {
current_set_id: None,
extra_weight: Weight::zero(),
extra_size: 0,
is_free_execution_expected: false,
},
SubmitParachainHeadsInfo {
at_relay_block_number: 200,
Expand Down Expand Up @@ -1246,6 +1249,7 @@ mod tests {
current_set_id: None,
extra_weight: Weight::zero(),
extra_size: 0,
is_free_execution_expected: false,
},
SubmitParachainHeadsInfo {
at_relay_block_number: 200,
Expand Down Expand Up @@ -1279,6 +1283,7 @@ mod tests {
current_set_id: None,
extra_weight: Weight::zero(),
extra_size: 0,
is_free_execution_expected: false,
},
MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo {
base: BaseMessagesProofInfo {
Expand Down Expand Up @@ -1311,6 +1316,7 @@ mod tests {
current_set_id: None,
extra_weight: Weight::zero(),
extra_size: 0,
is_free_execution_expected: false,
},
MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo(
BaseMessagesProofInfo {
Expand Down
Loading

0 comments on commit e901f46

Please sign in to comment.