Reduce the number of macros used for SignedExtensions

Reduce the number of macros used for SignedExtensions

Signed-off-by: Serban Iorga <serban@parity.io>
This commit is contained in:
Serban Iorga
2022-07-25 11:27:35 +03:00
committed by Bastian Köcher
parent 057fd6cab2
commit 5d9bd1d0b5
7 changed files with 403 additions and 406 deletions
+68 -124
View File
@@ -14,154 +14,98 @@
// 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/>.
/// Declares a runtime-specific `BridgeRejectObsoleteParachainHeader` signed extension.
///
/// ## Example
///
/// ```nocompile
/// pallet_bridge_grandpa::declare_bridge_reject_obsolete_parachain_header!{
/// Runtime,
/// Call::BridgeRialtoParachains => RialtoGrandpaInstance,
/// Call::BridgeWestendParachains => WestendGrandpaInstance,
/// }
/// ```
///
/// The goal of this extension is to avoid "mining" transactions that provide
/// outdated bridged parachain heads. Without that extension, even honest relayers
use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use bp_runtime::FilterCall;
use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction};
/// Validate parachain heads in order to avoid "mining" transactions that provide
/// outdated bridged parachain heads. Without this validation, even honest relayers
/// may lose their funds if there are multiple relays running and submitting the
/// same information.
///
/// This extension only works with transactions that are updating single parachain
/// This validation only works with transactions that are updating single parachain
/// head. We can't use unbounded validation - it may take too long and either break
/// block production, or "eat" significant portion of block production time literally
/// for nothing. In addition, the single-parachain-head-per-transaction is how the
/// pallet will be used in our environment.
#[macro_export]
macro_rules! declare_bridge_reject_obsolete_parachain_header {
($runtime:ident, $($call:path => $instance:ty),*) => {
/// Transaction-with-obsolete-bridged-parachain-header check that will reject transaction if
/// it submits obsolete bridged parachain header.
#[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)]
pub struct BridgeRejectObsoleteParachainHeader;
impl<
Call: IsSubType<CallableCallFor<Pallet<T, I>, T>>,
T: frame_system::Config<Call = Call> + Config<I>,
I: 'static,
> FilterCall<Call> for Pallet<T, I>
where
<T as pallet_bridge_grandpa::Config<T::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<
BlockNumber = RelayBlockNumber,
Hash = RelayBlockHash,
Hasher = RelayBlockHasher,
>,
{
fn validate(call: &Call) -> TransactionValidity {
let (bundled_relay_block_number, parachains) = match call.is_sub_type() {
Some(crate::Call::<T, I>::submit_parachain_heads {
ref at_relay_block,
ref parachains,
..
}) if parachains.len() == 1 => (at_relay_block.0, parachains),
_ => return Ok(ValidTransaction::default()),
};
impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteParachainHeader {
const IDENTIFIER: &'static str = "BridgeRejectObsoleteParachainHeader";
type AccountId = <$runtime as frame_system::Config>::AccountId;
type Call = <$runtime as frame_system::Config>::Call;
type AdditionalSigned = ();
type Pre = ();
let (parachain, parachain_head_hash) =
parachains.get(0).expect("verified by match condition; qed");
let best_parachain_head = crate::BestParaHeads::<T, I>::get(parachain);
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<Self::Call>,
_len: usize,
) -> sp_runtime::transaction_validity::TransactionValidity {
match *call {
$(
$call($crate::Call::<$runtime, $instance>::submit_parachain_heads {
ref at_relay_block,
ref parachains,
..
}) if parachains.len() == 1 => {
let (parachain, parachain_head_hash) = parachains.get(0).expect("verified by match condition; qed");
let bundled_relay_block_number = at_relay_block.0;
let best_parachain_head = $crate::BestParaHeads::<$runtime, $instance>::get(parachain);
match best_parachain_head {
Some(best_parachain_head) if best_parachain_head.at_relay_block_number
>= bundled_relay_block_number => {
log::trace!(
target: $crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: bundled relay block number: \
{:?} best relay block number: {:?}",
parachain,
bundled_relay_block_number,
best_parachain_head.at_relay_block_number,
);
sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
}
Some(best_parachain_head) if best_parachain_head.head_hash == *parachain_head_hash => {
log::trace!(
target: $crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: head hash {:?}",
parachain,
best_parachain_head.head_hash,
);
sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
}
_ => Ok(sp_runtime::transaction_validity::ValidTransaction::default()),
}
},
)*
_ => Ok(sp_runtime::transaction_validity::ValidTransaction::default()),
}
}
fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
self.validate(who, call, info, len).map(drop)
}
fn post_dispatch(
_maybe_pre: Option<Self::Pre>,
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
_post_info: &sp_runtime::traits::PostDispatchInfoOf<Self::Call>,
_len: usize,
_result: &sp_runtime::DispatchResult,
) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> {
Ok(())
}
match best_parachain_head {
Some(best_parachain_head)
if best_parachain_head.at_relay_block_number >= bundled_relay_block_number =>
{
log::trace!(
target: crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: \
bundled relay block number: {:?} \
best relay block number: {:?}",
parachain,
bundled_relay_block_number,
best_parachain_head.at_relay_block_number,
);
InvalidTransaction::Stale.into()
},
Some(best_parachain_head) if best_parachain_head.head_hash == *parachain_head_hash => {
log::trace!(
target: crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: head hash {:?}",
parachain,
best_parachain_head.head_hash,
);
InvalidTransaction::Stale.into()
},
_ => Ok(ValidTransaction::default()),
}
};
}
}
#[cfg(test)]
mod tests {
use crate::{
extension::FilterCall,
mock::{run_test, Call, TestRuntime},
BestParaHead, BestParaHeads, RelayBlockNumber,
};
use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
use frame_support::weights::{DispatchClass, DispatchInfo, Pays};
use sp_runtime::traits::SignedExtension;
declare_bridge_reject_obsolete_parachain_header! {
TestRuntime,
Call::Parachains => ()
}
fn validate_submit_parachain_heads(
num: RelayBlockNumber,
parachains: Vec<(ParaId, ParaHash)>,
) -> bool {
BridgeRejectObsoleteParachainHeader
.validate(
&42,
&Call::Parachains(crate::Call::<TestRuntime, ()>::submit_parachain_heads {
at_relay_block: (num, Default::default()),
parachains,
parachain_heads_proof: ParaHeadsProof(Vec::new()),
}),
&DispatchInfo { weight: 0, class: DispatchClass::Operational, pays_fee: Pays::Yes },
0,
)
.is_ok()
crate::Pallet::<TestRuntime>::validate(&Call::Parachains(
crate::Call::<TestRuntime, ()>::submit_parachain_heads {
at_relay_block: (num, Default::default()),
parachains,
parachain_heads_proof: ParaHeadsProof(Vec::new()),
},
))
.is_ok()
}
fn sync_to_relay_header_10() {