mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
Bridge: make some headers submissions free (#4102)
supersedes https://github.com/paritytech/parity-bridges-common/pull/2873 Draft because of couple of TODOs: - [x] fix remaining TODOs; - [x] double check that all changes from https://github.com/paritytech/parity-bridges-common/pull/2873 are correctly ported; - [x] create a separate PR (on top of that one or a follow up?) for https://github.com/paritytech/polkadot-sdk/tree/sv-try-new-bridge-fees; - [x] fix compilation issues (haven't checked, but there should be many). --------- Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
committed by
GitHub
parent
4f3d43a0c4
commit
a633e954f3
@@ -18,55 +18,229 @@
|
||||
//! 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;
|
||||
use crate::{
|
||||
extensions::refund_relayer_extension::RefundableParachainId,
|
||||
messages_call_ext::MessagesCallSubType,
|
||||
};
|
||||
use bp_relayers::ExplicitOrAccountParams;
|
||||
use bp_runtime::Parachain;
|
||||
use pallet_bridge_grandpa::{
|
||||
BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper,
|
||||
};
|
||||
use pallet_bridge_parachains::{
|
||||
CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo,
|
||||
};
|
||||
use pallet_bridge_relayers::Pallet as RelayersPallet;
|
||||
use sp_runtime::{
|
||||
traits::{Get, PhantomData, UniqueSaturatedInto},
|
||||
transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder},
|
||||
};
|
||||
|
||||
/// 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;
|
||||
pub trait BridgeRuntimeFilterCall<AccountId, Call> {
|
||||
/// Data that may be passed from the validate to `post_dispatch`.
|
||||
type ToPostDispatch;
|
||||
/// Called during validation. Needs to checks whether a runtime call, submitted
|
||||
/// by the `who` is valid. `who` may be `None` if transaction is not signed
|
||||
/// by a regular account.
|
||||
fn validate(who: &AccountId, call: &Call) -> (Self::ToPostDispatch, TransactionValidity);
|
||||
/// Called after transaction is dispatched.
|
||||
fn post_dispatch(_who: &AccountId, _has_failed: bool, _to_post_dispatch: Self::ToPostDispatch) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I>
|
||||
/// Wrapper for the bridge GRANDPA pallet that checks calls for obsolete submissions
|
||||
/// and also boosts transaction priority if it has submitted by registered relayer.
|
||||
/// The boost is computed as
|
||||
/// `(BundledHeaderNumber - 1 - BestFinalizedHeaderNumber) * Priority::get()`.
|
||||
/// The boost is only applied if submitter has active registration in the relayers
|
||||
/// pallet.
|
||||
pub struct CheckAndBoostBridgeGrandpaTransactions<T, I, Priority, SlashAccount>(
|
||||
PhantomData<(T, I, Priority, SlashAccount)>,
|
||||
);
|
||||
|
||||
impl<T, I: 'static, Priority: Get<TransactionPriority>, SlashAccount: Get<T::AccountId>>
|
||||
BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
|
||||
for CheckAndBoostBridgeGrandpaTransactions<T, I, Priority, SlashAccount>
|
||||
where
|
||||
T: pallet_bridge_relayers::Config + pallet_bridge_grandpa::Config<I>,
|
||||
T::RuntimeCall: GrandpaCallSubType<T, I>,
|
||||
{
|
||||
// bridged header number, bundled in transaction
|
||||
type ToPostDispatch = Option<BridgedBlockNumber<T, I>>;
|
||||
|
||||
fn validate(
|
||||
who: &T::AccountId,
|
||||
call: &T::RuntimeCall,
|
||||
) -> (Self::ToPostDispatch, TransactionValidity) {
|
||||
match GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call) {
|
||||
Ok(Some(our_tx)) => {
|
||||
let to_post_dispatch = Some(our_tx.base.block_number);
|
||||
let total_priority_boost =
|
||||
compute_priority_boost::<T, _, Priority>(who, our_tx.improved_by);
|
||||
(
|
||||
to_post_dispatch,
|
||||
ValidTransactionBuilder::default().priority(total_priority_boost).build(),
|
||||
)
|
||||
},
|
||||
Ok(None) => (None, ValidTransactionBuilder::default().build()),
|
||||
Err(e) => (None, Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn post_dispatch(
|
||||
relayer: &T::AccountId,
|
||||
has_failed: bool,
|
||||
bundled_block_number: Self::ToPostDispatch,
|
||||
) {
|
||||
// we are only interested in associated pallet submissions
|
||||
let Some(bundled_block_number) = bundled_block_number else { return };
|
||||
// we are only interested in failed or unneeded transactions
|
||||
let has_failed =
|
||||
has_failed || !SubmitFinalityProofHelper::<T, I>::was_successful(bundled_block_number);
|
||||
|
||||
if !has_failed {
|
||||
return
|
||||
}
|
||||
|
||||
// let's slash registered relayer
|
||||
RelayersPallet::<T>::slash_and_deregister(
|
||||
relayer,
|
||||
ExplicitOrAccountParams::Explicit(SlashAccount::get()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for the bridge parachains pallet that checks calls for obsolete submissions
|
||||
/// and also boosts transaction priority if it has submitted by registered relayer.
|
||||
/// The boost is computed as
|
||||
/// `(BundledHeaderNumber - 1 - BestKnownHeaderNumber) * Priority::get()`.
|
||||
/// The boost is only applied if submitter has active registration in the relayers
|
||||
/// pallet.
|
||||
pub struct CheckAndBoostBridgeParachainsTransactions<T, RefPara, Priority, SlashAccount>(
|
||||
PhantomData<(T, RefPara, Priority, SlashAccount)>,
|
||||
);
|
||||
|
||||
impl<T, RefPara, Priority: Get<TransactionPriority>, SlashAccount: Get<T::AccountId>>
|
||||
BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall>
|
||||
for CheckAndBoostBridgeParachainsTransactions<T, RefPara, Priority, SlashAccount>
|
||||
where
|
||||
T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config<RefPara::Instance>,
|
||||
RefPara: RefundableParachainId,
|
||||
T::RuntimeCall: ParachainsCallSubtype<T, RefPara::Instance>,
|
||||
{
|
||||
// bridged header number, bundled in transaction
|
||||
type ToPostDispatch = Option<SubmitParachainHeadsInfo>;
|
||||
|
||||
fn validate(
|
||||
who: &T::AccountId,
|
||||
call: &T::RuntimeCall,
|
||||
) -> (Self::ToPostDispatch, TransactionValidity) {
|
||||
match ParachainsCallSubtype::<T, RefPara::Instance>::check_obsolete_submit_parachain_heads(
|
||||
call,
|
||||
) {
|
||||
Ok(Some(our_tx)) if our_tx.base.para_id.0 == RefPara::BridgedChain::PARACHAIN_ID => {
|
||||
let to_post_dispatch = Some(our_tx.base);
|
||||
let total_priority_boost =
|
||||
compute_priority_boost::<T, _, Priority>(&who, our_tx.improved_by);
|
||||
(
|
||||
to_post_dispatch,
|
||||
ValidTransactionBuilder::default().priority(total_priority_boost).build(),
|
||||
)
|
||||
},
|
||||
Ok(_) => (None, ValidTransactionBuilder::default().build()),
|
||||
Err(e) => (None, Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn post_dispatch(relayer: &T::AccountId, has_failed: bool, maybe_update: Self::ToPostDispatch) {
|
||||
// we are only interested in associated pallet submissions
|
||||
let Some(update) = maybe_update else { return };
|
||||
// we are only interested in failed or unneeded transactions
|
||||
let has_failed = has_failed ||
|
||||
!SubmitParachainHeadsHelper::<T, RefPara::Instance>::was_successful(&update);
|
||||
|
||||
if !has_failed {
|
||||
return
|
||||
}
|
||||
|
||||
// let's slash registered relayer
|
||||
RelayersPallet::<T>::slash_and_deregister(
|
||||
relayer,
|
||||
ExplicitOrAccountParams::Explicit(SlashAccount::get()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: 'static> BridgeRuntimeFilterCall<T::AccountId, 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)
|
||||
type ToPostDispatch = ();
|
||||
fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
|
||||
(
|
||||
(),
|
||||
GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call)
|
||||
.and_then(|_| ValidTransactionBuilder::default().build()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
|
||||
impl<T, I: 'static> BridgeRuntimeFilterCall<T::AccountId, 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)
|
||||
type ToPostDispatch = ();
|
||||
fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
|
||||
(
|
||||
(),
|
||||
ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call)
|
||||
.and_then(|_| ValidTransactionBuilder::default().build()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
|
||||
for pallet_bridge_messages::Pallet<T, I>
|
||||
impl<T: pallet_bridge_messages::Config<I>, I: 'static>
|
||||
BridgeRuntimeFilterCall<T::AccountId, T::RuntimeCall> for pallet_bridge_messages::Pallet<T, I>
|
||||
where
|
||||
T::RuntimeCall: MessagesCallSubType<T, I>,
|
||||
{
|
||||
type ToPostDispatch = ();
|
||||
/// 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()
|
||||
fn validate(_who: &T::AccountId, call: &T::RuntimeCall) -> ((), TransactionValidity) {
|
||||
((), call.check_obsolete_call())
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes priority boost that improved known header by `improved_by`
|
||||
fn compute_priority_boost<T, N, Priority>(
|
||||
relayer: &T::AccountId,
|
||||
improved_by: N,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
T: pallet_bridge_relayers::Config,
|
||||
N: UniqueSaturatedInto<TransactionPriority>,
|
||||
Priority: Get<TransactionPriority>,
|
||||
{
|
||||
// we only boost priority if relayer has staked required balance
|
||||
let is_relayer_registration_active = RelayersPallet::<T>::is_registration_active(relayer);
|
||||
// if tx improves by just one, there's no need to bump its priority
|
||||
let improved_by: TransactionPriority = improved_by.unique_saturated_into().saturating_sub(1);
|
||||
// if relayer is registered, for every skipped header we improve by `Priority`
|
||||
let boost_per_header = if is_relayer_registration_active { Priority::get() } else { 0 };
|
||||
improved_by.saturating_mul(boost_per_header)
|
||||
}
|
||||
|
||||
/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension.
|
||||
///
|
||||
/// ## Example
|
||||
@@ -92,7 +266,15 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
|
||||
type AccountId = $account_id;
|
||||
type Call = $call;
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
type Pre = (
|
||||
$account_id,
|
||||
( $(
|
||||
<$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<
|
||||
$account_id,
|
||||
$call,
|
||||
>>::ToPostDispatch,
|
||||
)* ),
|
||||
);
|
||||
|
||||
fn additional_signed(&self) -> sp_std::result::Result<
|
||||
(),
|
||||
@@ -101,29 +283,72 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn validate(
|
||||
&self,
|
||||
_who: &Self::AccountId,
|
||||
who: &Self::AccountId,
|
||||
call: &Self::Call,
|
||||
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> sp_runtime::transaction_validity::TransactionValidity {
|
||||
let valid = sp_runtime::transaction_validity::ValidTransaction::default();
|
||||
let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default();
|
||||
let to_prepare = ();
|
||||
$(
|
||||
let valid = valid
|
||||
.combine_with(<$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<$call>>::validate(call)?);
|
||||
let (from_validate, call_filter_validity) = <
|
||||
$filter_call as
|
||||
$crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<
|
||||
Self::AccountId,
|
||||
$call,
|
||||
>>::validate(&who, call);
|
||||
let tx_validity = tx_validity.combine_with(call_filter_validity?);
|
||||
)*
|
||||
Ok(valid)
|
||||
Ok(tx_validity)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
who: &Self::AccountId,
|
||||
relayer: &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)
|
||||
use tuplex::PushBack;
|
||||
let to_post_dispatch = ();
|
||||
$(
|
||||
let (from_validate, call_filter_validity) = <
|
||||
$filter_call as
|
||||
$crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<
|
||||
$account_id,
|
||||
$call,
|
||||
>>::validate(&relayer, call);
|
||||
let _ = call_filter_validity?;
|
||||
let to_post_dispatch = to_post_dispatch.push_back(from_validate);
|
||||
)*
|
||||
Ok((relayer.clone(), to_post_dispatch))
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn post_dispatch(
|
||||
to_post_dispatch: 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> {
|
||||
use tuplex::PopFront;
|
||||
let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) };
|
||||
let has_failed = result.is_err();
|
||||
$(
|
||||
let (item, to_post_dispatch) = to_post_dispatch.pop_front();
|
||||
<
|
||||
$filter_call as
|
||||
$crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<
|
||||
$account_id,
|
||||
$call,
|
||||
>>::post_dispatch(&relayer, has_failed, item);
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -132,10 +357,23 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
extensions::refund_relayer_extension::{
|
||||
tests::{
|
||||
initialize_environment, relayer_account_at_this_chain,
|
||||
submit_parachain_head_call_ex, submit_relay_header_call_ex,
|
||||
},
|
||||
RefundableParachain,
|
||||
},
|
||||
mock::*,
|
||||
};
|
||||
use bp_polkadot_core::parachains::ParaId;
|
||||
use bp_runtime::HeaderId;
|
||||
use frame_support::{assert_err, assert_ok};
|
||||
use sp_runtime::{
|
||||
traits::SignedExtension,
|
||||
traits::{ConstU64, SignedExtension},
|
||||
transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
|
||||
DispatchError,
|
||||
};
|
||||
|
||||
pub struct MockCall {
|
||||
@@ -143,7 +381,7 @@ mod tests {
|
||||
}
|
||||
|
||||
impl sp_runtime::traits::Dispatchable for MockCall {
|
||||
type RuntimeOrigin = ();
|
||||
type RuntimeOrigin = u64;
|
||||
type Config = ();
|
||||
type Info = ();
|
||||
type PostInfo = ();
|
||||
@@ -156,50 +394,287 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
struct FirstFilterCall;
|
||||
impl BridgeRuntimeFilterCall<MockCall> for FirstFilterCall {
|
||||
fn validate(call: &MockCall) -> TransactionValidity {
|
||||
if call.data <= 1 {
|
||||
return InvalidTransaction::Custom(1).into()
|
||||
}
|
||||
pub struct FirstFilterCall;
|
||||
impl FirstFilterCall {
|
||||
fn post_dispatch_called_with(success: bool) {
|
||||
frame_support::storage::unhashed::put(&[1], &success);
|
||||
}
|
||||
|
||||
Ok(ValidTransaction { priority: 1, ..Default::default() })
|
||||
fn verify_post_dispatch_called_with(success: bool) {
|
||||
assert_eq!(frame_support::storage::unhashed::get::<bool>(&[1]), Some(success));
|
||||
}
|
||||
}
|
||||
|
||||
struct SecondFilterCall;
|
||||
impl BridgeRuntimeFilterCall<MockCall> for SecondFilterCall {
|
||||
fn validate(call: &MockCall) -> TransactionValidity {
|
||||
if call.data <= 2 {
|
||||
return InvalidTransaction::Custom(2).into()
|
||||
impl BridgeRuntimeFilterCall<u64, MockCall> for FirstFilterCall {
|
||||
type ToPostDispatch = u64;
|
||||
fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
|
||||
if call.data <= 1 {
|
||||
return (1, InvalidTransaction::Custom(1).into())
|
||||
}
|
||||
|
||||
Ok(ValidTransaction { priority: 2, ..Default::default() })
|
||||
(1, Ok(ValidTransaction { priority: 1, ..Default::default() }))
|
||||
}
|
||||
|
||||
fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
|
||||
Self::post_dispatch_called_with(!has_failed);
|
||||
assert_eq!(to_post_dispatch, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SecondFilterCall;
|
||||
|
||||
impl SecondFilterCall {
|
||||
fn post_dispatch_called_with(success: bool) {
|
||||
frame_support::storage::unhashed::put(&[2], &success);
|
||||
}
|
||||
|
||||
fn verify_post_dispatch_called_with(success: bool) {
|
||||
assert_eq!(frame_support::storage::unhashed::get::<bool>(&[2]), Some(success));
|
||||
}
|
||||
}
|
||||
|
||||
impl BridgeRuntimeFilterCall<u64, MockCall> for SecondFilterCall {
|
||||
type ToPostDispatch = u64;
|
||||
fn validate(_who: &u64, call: &MockCall) -> (u64, TransactionValidity) {
|
||||
if call.data <= 2 {
|
||||
return (2, InvalidTransaction::Custom(2).into())
|
||||
}
|
||||
|
||||
(2, Ok(ValidTransaction { priority: 2, ..Default::default() }))
|
||||
}
|
||||
|
||||
fn post_dispatch(_who: &u64, has_failed: bool, to_post_dispatch: Self::ToPostDispatch) {
|
||||
Self::post_dispatch_called_with(!has_failed);
|
||||
assert_eq!(to_post_dispatch, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
fn test_generated_obsolete_extension() {
|
||||
generate_bridge_reject_obsolete_headers_and_messages!(
|
||||
MockCall,
|
||||
(),
|
||||
u64,
|
||||
FirstFilterCall,
|
||||
SecondFilterCall
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0),
|
||||
InvalidTransaction::Custom(1)
|
||||
);
|
||||
run_test(|| {
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 1 }, &(), 0),
|
||||
InvalidTransaction::Custom(1)
|
||||
);
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.pre_dispatch(
|
||||
&42,
|
||||
&MockCall { data: 1 },
|
||||
&(),
|
||||
0
|
||||
),
|
||||
InvalidTransaction::Custom(1)
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0),
|
||||
InvalidTransaction::Custom(2)
|
||||
);
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 2 }, &(), 0),
|
||||
InvalidTransaction::Custom(2)
|
||||
);
|
||||
assert_err!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.pre_dispatch(
|
||||
&42,
|
||||
&MockCall { data: 2 },
|
||||
&(),
|
||||
0
|
||||
),
|
||||
InvalidTransaction::Custom(2)
|
||||
);
|
||||
|
||||
assert_ok!(
|
||||
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0),
|
||||
ValidTransaction { priority: 3, ..Default::default() }
|
||||
)
|
||||
assert_eq!(
|
||||
BridgeRejectObsoleteHeadersAndMessages
|
||||
.validate(&42, &MockCall { data: 3 }, &(), 0)
|
||||
.unwrap(),
|
||||
ValidTransaction { priority: 3, ..Default::default() },
|
||||
);
|
||||
assert_eq!(
|
||||
BridgeRejectObsoleteHeadersAndMessages
|
||||
.pre_dispatch(&42, &MockCall { data: 3 }, &(), 0)
|
||||
.unwrap(),
|
||||
(42, (1, 2)),
|
||||
);
|
||||
|
||||
// when post_dispatch is called with `Ok(())`, it is propagated to all "nested"
|
||||
// extensions
|
||||
assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch(
|
||||
Some((0, (1, 2))),
|
||||
&(),
|
||||
&(),
|
||||
0,
|
||||
&Ok(())
|
||||
));
|
||||
FirstFilterCall::verify_post_dispatch_called_with(true);
|
||||
SecondFilterCall::verify_post_dispatch_called_with(true);
|
||||
|
||||
// when post_dispatch is called with `Err(())`, it is propagated to all "nested"
|
||||
// extensions
|
||||
assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch(
|
||||
Some((0, (1, 2))),
|
||||
&(),
|
||||
&(),
|
||||
0,
|
||||
&Err(DispatchError::BadOrigin)
|
||||
));
|
||||
FirstFilterCall::verify_post_dispatch_called_with(false);
|
||||
SecondFilterCall::verify_post_dispatch_called_with(false);
|
||||
});
|
||||
}
|
||||
|
||||
frame_support::parameter_types! {
|
||||
pub SlashDestination: ThisChainAccountId = 42;
|
||||
}
|
||||
|
||||
type BridgeGrandpaWrapper =
|
||||
CheckAndBoostBridgeGrandpaTransactions<TestRuntime, (), ConstU64<1_000>, SlashDestination>;
|
||||
|
||||
#[test]
|
||||
fn grandpa_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
let priority_boost = BridgeGrandpaWrapper::validate(
|
||||
&relayer_account_at_this_chain(),
|
||||
&submit_relay_header_call_ex(200),
|
||||
)
|
||||
.1
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority_boost, 0);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grandpa_wrapper_boosts_extensions_for_registered_relayer() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
let priority_boost = BridgeGrandpaWrapper::validate(
|
||||
&relayer_account_at_this_chain(),
|
||||
&submit_relay_header_call_ex(200),
|
||||
)
|
||||
.1
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority_boost, 99_000);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grandpa_wrapper_slashes_registered_relayer_if_transaction_fails() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), true, Some(150));
|
||||
assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grandpa_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
BridgeGrandpaWrapper::post_dispatch(&relayer_account_at_this_chain(), false, Some(100));
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
})
|
||||
}
|
||||
|
||||
type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions<
|
||||
TestRuntime,
|
||||
RefundableParachain<(), BridgedUnderlyingParachain>,
|
||||
ConstU64<1_000>,
|
||||
SlashDestination,
|
||||
>;
|
||||
|
||||
#[test]
|
||||
fn parachains_wrapper_does_not_boost_extensions_for_unregistered_relayer() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
let priority_boost = BridgeParachainsWrapper::validate(
|
||||
&relayer_account_at_this_chain(),
|
||||
&submit_parachain_head_call_ex(200),
|
||||
)
|
||||
.1
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority_boost, 0);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parachains_wrapper_boosts_extensions_for_registered_relayer() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
let priority_boost = BridgeParachainsWrapper::validate(
|
||||
&relayer_account_at_this_chain(),
|
||||
&submit_parachain_head_call_ex(200),
|
||||
)
|
||||
.1
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority_boost, 99_000);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parachains_wrapper_slashes_registered_relayer_if_transaction_fails() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
BridgeParachainsWrapper::post_dispatch(
|
||||
&relayer_account_at_this_chain(),
|
||||
true,
|
||||
Some(SubmitParachainHeadsInfo {
|
||||
at_relay_block: HeaderId(150, Default::default()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [150u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
}),
|
||||
);
|
||||
assert!(!BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parachains_wrapper_does_not_slash_registered_relayer_if_transaction_succeeds() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
BridgeParachainsWrapper::post_dispatch(
|
||||
&relayer_account_at_this_chain(),
|
||||
false,
|
||||
Some(SubmitParachainHeadsInfo {
|
||||
at_relay_block: HeaderId(100, Default::default()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [100u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
}),
|
||||
);
|
||||
assert!(BridgeRelayers::is_registration_active(&relayer_account_at_this_chain()));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will
|
||||
//! be rejected. This can lower bridge throughput down to one message per block.
|
||||
|
||||
use bp_messages::MessageNonce;
|
||||
use frame_support::traits::Get;
|
||||
use sp_runtime::transaction_validity::TransactionPriority;
|
||||
|
||||
@@ -30,16 +29,19 @@ use sp_runtime::transaction_validity::TransactionPriority;
|
||||
#[allow(unused_imports)]
|
||||
pub use integrity_tests::*;
|
||||
|
||||
/// Compute priority boost for message delivery transaction that delivers
|
||||
/// given number of messages.
|
||||
pub fn compute_priority_boost<PriorityBoostPerMessage>(
|
||||
messages: MessageNonce,
|
||||
) -> TransactionPriority
|
||||
/// We'll deal with different bridge items here - messages, headers, ...
|
||||
/// To avoid being too verbose with generic code, let's just define a separate alias.
|
||||
pub type ItemCount = u64;
|
||||
|
||||
/// Compute priority boost for transaction that brings given number of bridge
|
||||
/// items (messages, headers, ...), when every additional item adds `PriorityBoostPerItem`
|
||||
/// to transaction priority.
|
||||
pub fn compute_priority_boost<PriorityBoostPerItem>(n_items: ItemCount) -> TransactionPriority
|
||||
where
|
||||
PriorityBoostPerMessage: Get<TransactionPriority>,
|
||||
PriorityBoostPerItem: Get<TransactionPriority>,
|
||||
{
|
||||
// we don't want any boost for transaction with single message => minus one
|
||||
PriorityBoostPerMessage::get().saturating_mul(messages.saturating_sub(1))
|
||||
// we don't want any boost for transaction with single (additional) item => minus one
|
||||
PriorityBoostPerItem::get().saturating_mul(n_items.saturating_sub(1))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "integrity-test"))]
|
||||
@@ -47,7 +49,8 @@ mod integrity_tests {}
|
||||
|
||||
#[cfg(feature = "integrity-test")]
|
||||
mod integrity_tests {
|
||||
use super::compute_priority_boost;
|
||||
use super::{compute_priority_boost, ItemCount};
|
||||
use crate::extensions::refund_relayer_extension::RefundableParachainId;
|
||||
|
||||
use bp_messages::MessageNonce;
|
||||
use bp_runtime::PreComputedSize;
|
||||
@@ -55,7 +58,6 @@ mod integrity_tests {
|
||||
dispatch::{DispatchClass, DispatchInfo, Pays, PostDispatchInfo},
|
||||
traits::Get,
|
||||
};
|
||||
use pallet_bridge_messages::WeightInfoExt;
|
||||
use pallet_transaction_payment::OnChargeTransaction;
|
||||
use sp_runtime::{
|
||||
traits::{Dispatchable, UniqueSaturatedInto, Zero},
|
||||
@@ -68,37 +70,33 @@ mod integrity_tests {
|
||||
T,
|
||||
>>::Balance;
|
||||
|
||||
/// Ensures that the value of `PriorityBoostPerMessage` matches the value of
|
||||
/// `tip_boost_per_message`.
|
||||
/// Ensures that the value of `PriorityBoostPerItem` matches the value of
|
||||
/// `tip_boost_per_item`.
|
||||
///
|
||||
/// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost
|
||||
/// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure
|
||||
/// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close
|
||||
/// We want two transactions, `TX1` with `N` items and `TX2` with `N+1` items, have almost
|
||||
/// the same priority if we'll add `tip_boost_per_item` tip to the `TX1`. We want to be sure
|
||||
/// that if we add plain `PriorityBoostPerItem` priority to `TX1`, the priority will be close
|
||||
/// to `TX2` as well.
|
||||
pub fn ensure_priority_boost_is_sane<Runtime, MessagesInstance, PriorityBoostPerMessage>(
|
||||
tip_boost_per_message: BalanceOf<Runtime>,
|
||||
fn ensure_priority_boost_is_sane<PriorityBoostPerItem, Balance>(
|
||||
param_name: &str,
|
||||
max_items: ItemCount,
|
||||
tip_boost_per_item: Balance,
|
||||
estimate_priority: impl Fn(ItemCount, Balance) -> TransactionPriority,
|
||||
) where
|
||||
Runtime:
|
||||
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
|
||||
MessagesInstance: 'static,
|
||||
PriorityBoostPerMessage: Get<TransactionPriority>,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
PriorityBoostPerItem: Get<TransactionPriority>,
|
||||
ItemCount: UniqueSaturatedInto<Balance>,
|
||||
Balance: FixedPointOperand + Zero,
|
||||
{
|
||||
let priority_boost_per_message = PriorityBoostPerMessage::get();
|
||||
let maximal_messages_in_delivery_transaction =
|
||||
Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
|
||||
for messages in 1..=maximal_messages_in_delivery_transaction {
|
||||
let base_priority = estimate_message_delivery_transaction_priority::<
|
||||
Runtime,
|
||||
MessagesInstance,
|
||||
>(messages, Zero::zero());
|
||||
let priority_boost = compute_priority_boost::<PriorityBoostPerMessage>(messages);
|
||||
let priority_with_boost = base_priority + priority_boost;
|
||||
let priority_boost_per_item = PriorityBoostPerItem::get();
|
||||
for n_items in 1..=max_items {
|
||||
let base_priority = estimate_priority(n_items, Zero::zero());
|
||||
let priority_boost = compute_priority_boost::<PriorityBoostPerItem>(n_items);
|
||||
let priority_with_boost = base_priority
|
||||
.checked_add(priority_boost)
|
||||
.expect("priority overflow: try lowering `max_items` or `tip_boost_per_item`?");
|
||||
|
||||
let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into());
|
||||
let priority_with_tip =
|
||||
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(1, tip);
|
||||
let tip = tip_boost_per_item.saturating_mul((n_items - 1).unique_saturated_into());
|
||||
let priority_with_tip = estimate_priority(1, tip);
|
||||
|
||||
const ERROR_MARGIN: TransactionPriority = 5; // 5%
|
||||
if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) /
|
||||
@@ -106,97 +104,304 @@ mod integrity_tests {
|
||||
ERROR_MARGIN
|
||||
{
|
||||
panic!(
|
||||
"The PriorityBoostPerMessage value ({}) must be fixed to: {}",
|
||||
priority_boost_per_message,
|
||||
compute_priority_boost_per_message::<Runtime, MessagesInstance>(
|
||||
tip_boost_per_message
|
||||
"The {param_name} value ({}) must be fixed to: {}",
|
||||
priority_boost_per_item,
|
||||
compute_priority_boost_per_item(
|
||||
max_items,
|
||||
tip_boost_per_item,
|
||||
estimate_priority
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute priority boost that we give to message delivery transaction for additional message.
|
||||
/// Compute priority boost that we give to bridge transaction for every
|
||||
/// additional bridge item.
|
||||
#[cfg(feature = "integrity-test")]
|
||||
fn compute_priority_boost_per_message<Runtime, MessagesInstance>(
|
||||
tip_boost_per_message: BalanceOf<Runtime>,
|
||||
fn compute_priority_boost_per_item<Balance>(
|
||||
max_items: ItemCount,
|
||||
tip_boost_per_item: Balance,
|
||||
estimate_priority: impl Fn(ItemCount, Balance) -> TransactionPriority,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
Runtime:
|
||||
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
|
||||
MessagesInstance: 'static,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
ItemCount: UniqueSaturatedInto<Balance>,
|
||||
Balance: FixedPointOperand + Zero,
|
||||
{
|
||||
// estimate priority of transaction that delivers one message and has large tip
|
||||
let maximal_messages_in_delivery_transaction =
|
||||
Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
|
||||
// estimate priority of transaction that delivers one item and has large tip
|
||||
let small_with_tip_priority =
|
||||
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(
|
||||
1,
|
||||
tip_boost_per_message
|
||||
.saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()),
|
||||
);
|
||||
// estimate priority of transaction that delivers maximal number of messages, but has no tip
|
||||
let large_without_tip_priority = estimate_message_delivery_transaction_priority::<
|
||||
Runtime,
|
||||
MessagesInstance,
|
||||
>(maximal_messages_in_delivery_transaction, Zero::zero());
|
||||
estimate_priority(1, tip_boost_per_item.saturating_mul(max_items.saturated_into()));
|
||||
// estimate priority of transaction that delivers maximal number of items, but has no tip
|
||||
let large_without_tip_priority = estimate_priority(max_items, Zero::zero());
|
||||
|
||||
small_with_tip_priority
|
||||
.saturating_sub(large_without_tip_priority)
|
||||
.saturating_div(maximal_messages_in_delivery_transaction - 1)
|
||||
.saturating_div(max_items - 1)
|
||||
}
|
||||
|
||||
/// Estimate message delivery transaction priority.
|
||||
#[cfg(feature = "integrity-test")]
|
||||
fn estimate_message_delivery_transaction_priority<Runtime, MessagesInstance>(
|
||||
messages: MessageNonce,
|
||||
tip: BalanceOf<Runtime>,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
Runtime:
|
||||
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
|
||||
MessagesInstance: 'static,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// just an estimation of extra transaction bytes that are added to every transaction
|
||||
// (including signature, signed extensions extra and etc + in our case it includes
|
||||
// all call arguments except the proof itself)
|
||||
let base_tx_size = 512;
|
||||
// let's say we are relaying similar small messages and for every message we add more trie
|
||||
// nodes to the proof (x0.5 because we expect some nodes to be reused)
|
||||
let estimated_message_size = 512;
|
||||
// let's say all our messages have the same dispatch weight
|
||||
let estimated_message_dispatch_weight =
|
||||
Runtime::WeightInfo::message_dispatch_weight(estimated_message_size);
|
||||
// messages proof argument size is (for every message) messages size + some additional
|
||||
// trie nodes. Some of them are reused by different messages, so let's take 2/3 of default
|
||||
// "overhead" constant
|
||||
let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size()
|
||||
.saturating_mul(2)
|
||||
.saturating_div(3)
|
||||
.saturating_add(estimated_message_size)
|
||||
.saturating_mul(messages as _);
|
||||
/// Computations, specific to bridge relay chains transactions.
|
||||
pub mod per_relay_header {
|
||||
use super::*;
|
||||
|
||||
// finally we are able to estimate transaction size and weight
|
||||
let transaction_size = base_tx_size.saturating_add(messages_proof_size);
|
||||
let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight(
|
||||
&PreComputedSize(transaction_size as _),
|
||||
messages as _,
|
||||
estimated_message_dispatch_weight.saturating_mul(messages),
|
||||
);
|
||||
use bp_header_chain::{
|
||||
max_expected_submit_finality_proof_arguments_size, ChainWithGrandpa,
|
||||
};
|
||||
use pallet_bridge_grandpa::WeightInfoExt;
|
||||
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
|
||||
&DispatchInfo {
|
||||
weight: transaction_weight,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
transaction_size as _,
|
||||
tip,
|
||||
Zero::zero(),
|
||||
)
|
||||
/// Ensures that the value of `PriorityBoostPerHeader` matches the value of
|
||||
/// `tip_boost_per_header`.
|
||||
///
|
||||
/// We want two transactions, `TX1` with `N` headers and `TX2` with `N+1` headers, have
|
||||
/// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want
|
||||
/// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority
|
||||
/// will be close to `TX2` as well.
|
||||
pub fn ensure_priority_boost_is_sane<Runtime, GrandpaInstance, PriorityBoostPerHeader>(
|
||||
tip_boost_per_header: BalanceOf<Runtime>,
|
||||
) where
|
||||
Runtime:
|
||||
pallet_transaction_payment::Config + pallet_bridge_grandpa::Config<GrandpaInstance>,
|
||||
GrandpaInstance: 'static,
|
||||
PriorityBoostPerHeader: Get<TransactionPriority>,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// the meaning of `max_items` here is different when comparing with message
|
||||
// transactions - with messages we have a strict limit on maximal number of
|
||||
// messages we can fit into a single transaction. With headers, current best
|
||||
// header may be improved by any "number of items". But this number is only
|
||||
// used to verify priority boost, so it should be fine to select this arbitrary
|
||||
// value - it SHALL NOT affect any value, it just adds more tests for the value.
|
||||
let maximal_improved_by = 4_096;
|
||||
super::ensure_priority_boost_is_sane::<PriorityBoostPerHeader, BalanceOf<Runtime>>(
|
||||
"PriorityBoostPerRelayHeader",
|
||||
maximal_improved_by,
|
||||
tip_boost_per_header,
|
||||
|_n_headers, tip| {
|
||||
estimate_relay_header_submit_transaction_priority::<Runtime, GrandpaInstance>(
|
||||
tip,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Estimate relay header delivery transaction priority.
|
||||
#[cfg(feature = "integrity-test")]
|
||||
fn estimate_relay_header_submit_transaction_priority<Runtime, GrandpaInstance>(
|
||||
tip: BalanceOf<Runtime>,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
Runtime:
|
||||
pallet_transaction_payment::Config + pallet_bridge_grandpa::Config<GrandpaInstance>,
|
||||
GrandpaInstance: 'static,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// just an estimation of extra transaction bytes that are added to every transaction
|
||||
// (including signature, signed extensions extra and etc + in our case it includes
|
||||
// all call arguments except the proof itself)
|
||||
let base_tx_size = 512;
|
||||
// let's say we are relaying largest relay chain headers
|
||||
let tx_call_size = max_expected_submit_finality_proof_arguments_size::<
|
||||
Runtime::BridgedChain,
|
||||
>(true, Runtime::BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1);
|
||||
|
||||
// finally we are able to estimate transaction size and weight
|
||||
let transaction_size = base_tx_size.saturating_add(tx_call_size);
|
||||
let transaction_weight = Runtime::WeightInfo::submit_finality_proof_weight(
|
||||
Runtime::BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1,
|
||||
Runtime::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY,
|
||||
);
|
||||
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
|
||||
&DispatchInfo {
|
||||
weight: transaction_weight,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
transaction_size as _,
|
||||
tip,
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Computations, specific to bridge parachains transactions.
|
||||
pub mod per_parachain_header {
|
||||
use super::*;
|
||||
|
||||
use bp_runtime::Parachain;
|
||||
use pallet_bridge_parachains::WeightInfoExt;
|
||||
|
||||
/// Ensures that the value of `PriorityBoostPerHeader` matches the value of
|
||||
/// `tip_boost_per_header`.
|
||||
///
|
||||
/// We want two transactions, `TX1` with `N` headers and `TX2` with `N+1` headers, have
|
||||
/// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want
|
||||
/// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority
|
||||
/// will be close to `TX2` as well.
|
||||
pub fn ensure_priority_boost_is_sane<Runtime, RefundableParachain, PriorityBoostPerHeader>(
|
||||
tip_boost_per_header: BalanceOf<Runtime>,
|
||||
) where
|
||||
Runtime: pallet_transaction_payment::Config
|
||||
+ pallet_bridge_parachains::Config<RefundableParachain::Instance>,
|
||||
RefundableParachain: RefundableParachainId,
|
||||
PriorityBoostPerHeader: Get<TransactionPriority>,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// the meaning of `max_items` here is different when comparing with message
|
||||
// transactions - with messages we have a strict limit on maximal number of
|
||||
// messages we can fit into a single transaction. With headers, current best
|
||||
// header may be improved by any "number of items". But this number is only
|
||||
// used to verify priority boost, so it should be fine to select this arbitrary
|
||||
// value - it SHALL NOT affect any value, it just adds more tests for the value.
|
||||
let maximal_improved_by = 4_096;
|
||||
super::ensure_priority_boost_is_sane::<PriorityBoostPerHeader, BalanceOf<Runtime>>(
|
||||
"PriorityBoostPerParachainHeader",
|
||||
maximal_improved_by,
|
||||
tip_boost_per_header,
|
||||
|_n_headers, tip| {
|
||||
estimate_parachain_header_submit_transaction_priority::<
|
||||
Runtime,
|
||||
RefundableParachain,
|
||||
>(tip)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Estimate parachain header delivery transaction priority.
|
||||
#[cfg(feature = "integrity-test")]
|
||||
fn estimate_parachain_header_submit_transaction_priority<Runtime, RefundableParachain>(
|
||||
tip: BalanceOf<Runtime>,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
Runtime: pallet_transaction_payment::Config
|
||||
+ pallet_bridge_parachains::Config<RefundableParachain::Instance>,
|
||||
RefundableParachain: RefundableParachainId,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// just an estimation of extra transaction bytes that are added to every transaction
|
||||
// (including signature, signed extensions extra and etc + in our case it includes
|
||||
// all call arguments except the proof itself)
|
||||
let base_tx_size = 512;
|
||||
// let's say we are relaying largest parachain headers and proof takes some more bytes
|
||||
let tx_call_size = <Runtime as pallet_bridge_parachains::Config<
|
||||
RefundableParachain::Instance,
|
||||
>>::WeightInfo::expected_extra_storage_proof_size()
|
||||
.saturating_add(RefundableParachain::BridgedChain::MAX_HEADER_SIZE);
|
||||
|
||||
// finally we are able to estimate transaction size and weight
|
||||
let transaction_size = base_tx_size.saturating_add(tx_call_size);
|
||||
let transaction_weight = <Runtime as pallet_bridge_parachains::Config<
|
||||
RefundableParachain::Instance,
|
||||
>>::WeightInfo::submit_parachain_heads_weight(
|
||||
Runtime::DbWeight::get(),
|
||||
&PreComputedSize(transaction_size as _),
|
||||
// just one parachain - all other submissions won't receive any boost
|
||||
1,
|
||||
);
|
||||
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
|
||||
&DispatchInfo {
|
||||
weight: transaction_weight,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
transaction_size as _,
|
||||
tip,
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Computations, specific to bridge messages transactions.
|
||||
pub mod per_message {
|
||||
use super::*;
|
||||
|
||||
use pallet_bridge_messages::WeightInfoExt;
|
||||
|
||||
/// Ensures that the value of `PriorityBoostPerMessage` matches the value of
|
||||
/// `tip_boost_per_message`.
|
||||
///
|
||||
/// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have
|
||||
/// almost the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want
|
||||
/// to be sure that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the
|
||||
/// priority will be close to `TX2` as well.
|
||||
pub fn ensure_priority_boost_is_sane<Runtime, MessagesInstance, PriorityBoostPerMessage>(
|
||||
tip_boost_per_message: BalanceOf<Runtime>,
|
||||
) where
|
||||
Runtime: pallet_transaction_payment::Config
|
||||
+ pallet_bridge_messages::Config<MessagesInstance>,
|
||||
MessagesInstance: 'static,
|
||||
PriorityBoostPerMessage: Get<TransactionPriority>,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
let maximal_messages_in_delivery_transaction =
|
||||
Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
|
||||
super::ensure_priority_boost_is_sane::<PriorityBoostPerMessage, BalanceOf<Runtime>>(
|
||||
"PriorityBoostPerMessage",
|
||||
maximal_messages_in_delivery_transaction,
|
||||
tip_boost_per_message,
|
||||
|n_messages, tip| {
|
||||
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(
|
||||
n_messages, tip,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Estimate message delivery transaction priority.
|
||||
#[cfg(feature = "integrity-test")]
|
||||
fn estimate_message_delivery_transaction_priority<Runtime, MessagesInstance>(
|
||||
messages: MessageNonce,
|
||||
tip: BalanceOf<Runtime>,
|
||||
) -> TransactionPriority
|
||||
where
|
||||
Runtime: pallet_transaction_payment::Config
|
||||
+ pallet_bridge_messages::Config<MessagesInstance>,
|
||||
MessagesInstance: 'static,
|
||||
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
|
||||
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
|
||||
{
|
||||
// just an estimation of extra transaction bytes that are added to every transaction
|
||||
// (including signature, signed extensions extra and etc + in our case it includes
|
||||
// all call arguments except the proof itself)
|
||||
let base_tx_size = 512;
|
||||
// let's say we are relaying similar small messages and for every message we add more
|
||||
// trie nodes to the proof (x0.5 because we expect some nodes to be reused)
|
||||
let estimated_message_size = 512;
|
||||
// let's say all our messages have the same dispatch weight
|
||||
let estimated_message_dispatch_weight =
|
||||
Runtime::WeightInfo::message_dispatch_weight(estimated_message_size);
|
||||
// messages proof argument size is (for every message) messages size + some additional
|
||||
// trie nodes. Some of them are reused by different messages, so let's take 2/3 of
|
||||
// default "overhead" constant
|
||||
let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size()
|
||||
.saturating_mul(2)
|
||||
.saturating_div(3)
|
||||
.saturating_add(estimated_message_size)
|
||||
.saturating_mul(messages as _);
|
||||
|
||||
// finally we are able to estimate transaction size and weight
|
||||
let transaction_size = base_tx_size.saturating_add(messages_proof_size);
|
||||
let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight(
|
||||
&PreComputedSize(transaction_size as _),
|
||||
messages as _,
|
||||
estimated_message_dispatch_weight.saturating_mul(messages),
|
||||
);
|
||||
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
|
||||
&DispatchInfo {
|
||||
weight: transaction_weight,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
transaction_size as _,
|
||||
tip,
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ use crate::messages_call_ext::{
|
||||
};
|
||||
use bp_messages::{LaneId, MessageNonce};
|
||||
use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams};
|
||||
use bp_runtime::{Chain, Parachain, ParachainIdOf, RangeInclusiveExt, StaticStrProvider};
|
||||
use bp_runtime::{Parachain, RangeInclusiveExt, StaticStrProvider};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use frame_support::{
|
||||
dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo},
|
||||
@@ -33,8 +33,7 @@ use frame_support::{
|
||||
CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
|
||||
};
|
||||
use pallet_bridge_grandpa::{
|
||||
CallSubType as GrandpaCallSubType, Config as GrandpaConfig, SubmitFinalityProofHelper,
|
||||
SubmitFinalityProofInfo,
|
||||
CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo,
|
||||
};
|
||||
use pallet_bridge_messages::Config as MessagesConfig;
|
||||
use pallet_bridge_parachains::{
|
||||
@@ -66,20 +65,9 @@ type CallOf<R> = <R as frame_system::Config>::RuntimeCall;
|
||||
/// coming from this parachain.
|
||||
pub trait RefundableParachainId {
|
||||
/// The instance of the bridge parachains pallet.
|
||||
type Instance;
|
||||
type Instance: 'static;
|
||||
/// The parachain Id.
|
||||
type Id: Get<u32>;
|
||||
}
|
||||
|
||||
/// Default implementation of `RefundableParachainId`.
|
||||
pub struct DefaultRefundableParachainId<Instance, Id>(PhantomData<(Instance, Id)>);
|
||||
|
||||
impl<Instance, Id> RefundableParachainId for DefaultRefundableParachainId<Instance, Id>
|
||||
where
|
||||
Id: Get<u32>,
|
||||
{
|
||||
type Instance = Instance;
|
||||
type Id = Id;
|
||||
type BridgedChain: Parachain;
|
||||
}
|
||||
|
||||
/// Implementation of `RefundableParachainId` for `trait Parachain`.
|
||||
@@ -87,10 +75,11 @@ pub struct RefundableParachain<Instance, Para>(PhantomData<(Instance, Para)>);
|
||||
|
||||
impl<Instance, Para> RefundableParachainId for RefundableParachain<Instance, Para>
|
||||
where
|
||||
Instance: 'static,
|
||||
Para: Parachain,
|
||||
{
|
||||
type Instance = Instance;
|
||||
type Id = ParachainIdOf<Para>;
|
||||
type BridgedChain = Para;
|
||||
}
|
||||
|
||||
/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages
|
||||
@@ -242,17 +231,10 @@ pub enum RelayerAccountAction<AccountId, Reward> {
|
||||
/// Everything common among our refund signed extensions.
|
||||
pub trait RefundSignedExtension:
|
||||
'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo
|
||||
where
|
||||
<Self::Runtime as GrandpaConfig<Self::GrandpaInstance>>::BridgedChain:
|
||||
Chain<BlockNumber = RelayBlockNumber>,
|
||||
{
|
||||
/// This chain runtime.
|
||||
type Runtime: UtilityConfig<RuntimeCall = CallOf<Self::Runtime>>
|
||||
+ GrandpaConfig<Self::GrandpaInstance>
|
||||
+ MessagesConfig<<Self::Msgs as RefundableMessagesLaneId>::Instance>
|
||||
type Runtime: MessagesConfig<<Self::Msgs as RefundableMessagesLaneId>::Instance>
|
||||
+ RelayersConfig;
|
||||
/// Grandpa pallet reference.
|
||||
type GrandpaInstance: 'static;
|
||||
/// Messages pallet and lane reference.
|
||||
type Msgs: RefundableMessagesLaneId;
|
||||
/// Refund amount calculator.
|
||||
@@ -276,11 +258,13 @@ where
|
||||
call: &CallOf<Self::Runtime>,
|
||||
) -> Result<&CallOf<Self::Runtime>, TransactionValidityError>;
|
||||
|
||||
/// Called from post-dispatch and shall perform additional checks (apart from relay
|
||||
/// chain finality and messages transaction finality) of given call result.
|
||||
/// Called from post-dispatch and shall perform additional checks (apart from messages
|
||||
/// transaction success) of given call result.
|
||||
fn additional_call_result_check(
|
||||
relayer: &AccountIdOf<Self::Runtime>,
|
||||
call_info: &CallInfo,
|
||||
extra_weight: &mut Weight,
|
||||
extra_size: &mut u32,
|
||||
) -> bool;
|
||||
|
||||
/// Given post-dispatch information, analyze the outcome of relayer call and return
|
||||
@@ -348,35 +332,6 @@ where
|
||||
return slash_relayer_if_delivery_result
|
||||
}
|
||||
|
||||
// check if relay chain state has been updated
|
||||
if let Some(finality_proof_info) = call_info.submit_finality_proof_info() {
|
||||
if !SubmitFinalityProofHelper::<Self::Runtime, Self::GrandpaInstance>::was_successful(
|
||||
finality_proof_info.block_number,
|
||||
) {
|
||||
// we only refund relayer if all calls have updated chain state
|
||||
log::trace!(
|
||||
target: "runtime::bridge",
|
||||
"{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof",
|
||||
Self::Id::STR,
|
||||
<Self::Msgs as RefundableMessagesLaneId>::Id::get(),
|
||||
relayer,
|
||||
);
|
||||
return slash_relayer_if_delivery_result
|
||||
}
|
||||
|
||||
// there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll`
|
||||
// transaction. If relay chain header is mandatory, the GRANDPA pallet returns
|
||||
// `Pays::No`, because such transaction is mandatory for operating the bridge. But
|
||||
// `utility.batchAll` transaction always requires payment. But in both cases we'll
|
||||
// refund relayer - either explicitly here, or using `Pays::No` if he's choosing
|
||||
// to submit dedicated transaction.
|
||||
|
||||
// submitter has means to include extra weight/bytes in the `submit_finality_proof`
|
||||
// call, so let's subtract extra weight/size to avoid refunding for this extra stuff
|
||||
extra_weight = finality_proof_info.extra_weight;
|
||||
extra_size = finality_proof_info.extra_size;
|
||||
}
|
||||
|
||||
// Check if the `ReceiveMessagesProof` call delivered at least some of the messages that
|
||||
// it contained. If this happens, we consider the transaction "helpful" and refund it.
|
||||
let msgs_call_info = call_info.messages_call_info();
|
||||
@@ -391,8 +346,13 @@ where
|
||||
return slash_relayer_if_delivery_result
|
||||
}
|
||||
|
||||
// do additional check
|
||||
if !Self::additional_call_result_check(&relayer, &call_info) {
|
||||
// do additional checks
|
||||
if !Self::additional_call_result_check(
|
||||
&relayer,
|
||||
&call_info,
|
||||
&mut extra_weight,
|
||||
&mut extra_size,
|
||||
) {
|
||||
return slash_relayer_if_delivery_result
|
||||
}
|
||||
|
||||
@@ -468,18 +428,11 @@ where
|
||||
RuntimeDebugNoBound,
|
||||
TypeInfo,
|
||||
)]
|
||||
pub struct RefundSignedExtensionAdapter<T: RefundSignedExtension>(T)
|
||||
where
|
||||
<T::Runtime as GrandpaConfig<T::GrandpaInstance>>::BridgedChain:
|
||||
Chain<BlockNumber = RelayBlockNumber>;
|
||||
pub struct RefundSignedExtensionAdapter<T: RefundSignedExtension>(T);
|
||||
|
||||
impl<T: RefundSignedExtension> SignedExtension for RefundSignedExtensionAdapter<T>
|
||||
where
|
||||
<T::Runtime as GrandpaConfig<T::GrandpaInstance>>::BridgedChain:
|
||||
Chain<BlockNumber = RelayBlockNumber>,
|
||||
CallOf<T::Runtime>: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
|
||||
+ IsSubType<CallableCallFor<UtilityPallet<T::Runtime>, T::Runtime>>
|
||||
+ GrandpaCallSubType<T::Runtime, T::GrandpaInstance>
|
||||
+ MessagesCallSubType<T::Runtime, <T::Msgs as RefundableMessagesLaneId>::Instance>,
|
||||
{
|
||||
const IDENTIFIER: &'static str = T::Id::STR;
|
||||
@@ -644,6 +597,14 @@ impl<Runtime, Para, Msgs, Refund, Priority, Id> RefundSignedExtension
|
||||
for RefundBridgedParachainMessages<Runtime, Para, Msgs, Refund, Priority, Id>
|
||||
where
|
||||
Self: 'static + Send + Sync,
|
||||
RefundBridgedGrandpaMessages<
|
||||
Runtime,
|
||||
Runtime::BridgesGrandpaPalletInstance,
|
||||
Msgs,
|
||||
Refund,
|
||||
Priority,
|
||||
Id,
|
||||
>: 'static + Send + Sync,
|
||||
Runtime: UtilityConfig<RuntimeCall = CallOf<Runtime>>
|
||||
+ BoundedBridgeGrandpaConfig<Runtime::BridgesGrandpaPalletInstance>
|
||||
+ ParachainsConfig<Para::Instance>
|
||||
@@ -661,7 +622,6 @@ where
|
||||
+ MessagesCallSubType<Runtime, Msgs::Instance>,
|
||||
{
|
||||
type Runtime = Runtime;
|
||||
type GrandpaInstance = Runtime::BridgesGrandpaPalletInstance;
|
||||
type Msgs = Msgs;
|
||||
type Refund = Refund;
|
||||
type Priority = Priority;
|
||||
@@ -687,7 +647,7 @@ where
|
||||
let para_finality_call = calls
|
||||
.next()
|
||||
.transpose()?
|
||||
.and_then(|c| c.submit_parachain_heads_info_for(Para::Id::get()));
|
||||
.and_then(|c| c.submit_parachain_heads_info_for(Para::BridgedChain::PARACHAIN_ID));
|
||||
let relay_finality_call =
|
||||
calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info());
|
||||
|
||||
@@ -711,7 +671,26 @@ where
|
||||
Ok(call)
|
||||
}
|
||||
|
||||
fn additional_call_result_check(relayer: &Runtime::AccountId, call_info: &CallInfo) -> bool {
|
||||
fn additional_call_result_check(
|
||||
relayer: &Runtime::AccountId,
|
||||
call_info: &CallInfo,
|
||||
extra_weight: &mut Weight,
|
||||
extra_size: &mut u32,
|
||||
) -> bool {
|
||||
// check if relay chain state has been updated
|
||||
let is_grandpa_call_successful =
|
||||
RefundBridgedGrandpaMessages::<
|
||||
Runtime,
|
||||
Runtime::BridgesGrandpaPalletInstance,
|
||||
Msgs,
|
||||
Refund,
|
||||
Priority,
|
||||
Id,
|
||||
>::additional_call_result_check(relayer, call_info, extra_weight, extra_size);
|
||||
if !is_grandpa_call_successful {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if parachain state has been updated
|
||||
if let Some(para_proof_info) = call_info.submit_parachain_heads_info() {
|
||||
if !SubmitParachainHeadsHelper::<Runtime, Para::Instance>::was_successful(
|
||||
@@ -722,7 +701,7 @@ where
|
||||
target: "runtime::bridge",
|
||||
"{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof",
|
||||
Id::STR,
|
||||
Para::Id::get(),
|
||||
Para::BridgedChain::PARACHAIN_ID,
|
||||
Msgs::Id::get(),
|
||||
relayer,
|
||||
);
|
||||
@@ -794,7 +773,6 @@ where
|
||||
+ MessagesCallSubType<Runtime, Msgs::Instance>,
|
||||
{
|
||||
type Runtime = Runtime;
|
||||
type GrandpaInstance = GrandpaInstance;
|
||||
type Msgs = Msgs;
|
||||
type Refund = Refund;
|
||||
type Priority = Priority;
|
||||
@@ -836,13 +814,125 @@ where
|
||||
Ok(call)
|
||||
}
|
||||
|
||||
fn additional_call_result_check(_relayer: &Runtime::AccountId, _call_info: &CallInfo) -> bool {
|
||||
fn additional_call_result_check(
|
||||
relayer: &Runtime::AccountId,
|
||||
call_info: &CallInfo,
|
||||
extra_weight: &mut Weight,
|
||||
extra_size: &mut u32,
|
||||
) -> bool {
|
||||
// check if relay chain state has been updated
|
||||
if let Some(finality_proof_info) = call_info.submit_finality_proof_info() {
|
||||
if !SubmitFinalityProofHelper::<Self::Runtime, GrandpaInstance>::was_successful(
|
||||
finality_proof_info.block_number,
|
||||
) {
|
||||
// we only refund relayer if all calls have updated chain state
|
||||
log::trace!(
|
||||
target: "runtime::bridge",
|
||||
"{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof",
|
||||
Self::Id::STR,
|
||||
<Self::Msgs as RefundableMessagesLaneId>::Id::get(),
|
||||
relayer,
|
||||
);
|
||||
return false
|
||||
}
|
||||
|
||||
// there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll`
|
||||
// transaction. If relay chain header is mandatory, the GRANDPA pallet returns
|
||||
// `Pays::No`, because such transaction is mandatory for operating the bridge. But
|
||||
// `utility.batchAll` transaction always requires payment. But in both cases we'll
|
||||
// refund relayer - either explicitly here, or using `Pays::No` if he's choosing
|
||||
// to submit dedicated transaction.
|
||||
|
||||
// submitter has means to include extra weight/bytes in the `submit_finality_proof`
|
||||
// call, so let's subtract extra weight/size to avoid refunding for this extra stuff
|
||||
*extra_weight = (*extra_weight).saturating_add(finality_proof_info.extra_weight);
|
||||
*extra_size = (*extra_size).saturating_add(finality_proof_info.extra_size);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation
|
||||
/// transactions. Finality transactions are not refunded.
|
||||
#[derive(
|
||||
DefaultNoBound,
|
||||
CloneNoBound,
|
||||
Decode,
|
||||
Encode,
|
||||
EqNoBound,
|
||||
PartialEqNoBound,
|
||||
RuntimeDebugNoBound,
|
||||
TypeInfo,
|
||||
)]
|
||||
#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))]
|
||||
pub struct RefundBridgedMessages<Runtime, Msgs, Refund, Priority, Id>(
|
||||
PhantomData<(
|
||||
// runtime with `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed
|
||||
Runtime,
|
||||
// implementation of `RefundableMessagesLaneId` trait, which specifies the instance of
|
||||
// the used `pallet-bridge-messages` pallet and the lane within this pallet
|
||||
Msgs,
|
||||
// implementation of the `RefundCalculator` trait, that is used to compute refund that
|
||||
// we give to relayer for his transaction
|
||||
Refund,
|
||||
// getter for per-message `TransactionPriority` boost that we give to message
|
||||
// delivery transactions
|
||||
Priority,
|
||||
// the runtime-unique identifier of this signed extension
|
||||
Id,
|
||||
)>,
|
||||
);
|
||||
|
||||
impl<Runtime, Msgs, Refund, Priority, Id> RefundSignedExtension
|
||||
for RefundBridgedMessages<Runtime, Msgs, Refund, Priority, Id>
|
||||
where
|
||||
Self: 'static + Send + Sync,
|
||||
Runtime: MessagesConfig<Msgs::Instance> + RelayersConfig,
|
||||
Msgs: RefundableMessagesLaneId,
|
||||
Refund: RefundCalculator<Balance = Runtime::Reward>,
|
||||
Priority: Get<TransactionPriority>,
|
||||
Id: StaticStrProvider,
|
||||
CallOf<Runtime>: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
|
||||
+ MessagesCallSubType<Runtime, Msgs::Instance>,
|
||||
{
|
||||
type Runtime = Runtime;
|
||||
type Msgs = Msgs;
|
||||
type Refund = Refund;
|
||||
type Priority = Priority;
|
||||
type Id = Id;
|
||||
|
||||
fn expand_call(call: &CallOf<Runtime>) -> Vec<&CallOf<Runtime>> {
|
||||
vec![call]
|
||||
}
|
||||
|
||||
fn parse_and_check_for_obsolete_call(
|
||||
call: &CallOf<Runtime>,
|
||||
) -> Result<Option<CallInfo>, TransactionValidityError> {
|
||||
let call = Self::check_obsolete_parsed_call(call)?;
|
||||
Ok(call.call_info_for(Msgs::Id::get()).map(CallInfo::Msgs))
|
||||
}
|
||||
|
||||
fn check_obsolete_parsed_call(
|
||||
call: &CallOf<Runtime>,
|
||||
) -> Result<&CallOf<Runtime>, TransactionValidityError> {
|
||||
call.check_obsolete_call()?;
|
||||
Ok(call)
|
||||
}
|
||||
|
||||
fn additional_call_result_check(
|
||||
_relayer: &Runtime::AccountId,
|
||||
_call_info: &CallInfo,
|
||||
_extra_weight: &mut Weight,
|
||||
_extra_size: &mut u32,
|
||||
) -> bool {
|
||||
// everything is checked by the `RefundTransactionExtension`
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
messages::{
|
||||
@@ -854,6 +944,7 @@ mod tests {
|
||||
},
|
||||
mock::*,
|
||||
};
|
||||
use bp_header_chain::StoredHeaderDataBuilder;
|
||||
use bp_messages::{
|
||||
DeliveredMessages, InboundLaneData, MessageNonce, MessagesOperatingMode, OutboundLaneData,
|
||||
UnrewardedRelayer, UnrewardedRelayersState,
|
||||
@@ -879,7 +970,6 @@ mod tests {
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
TestParachain: u32 = 1000;
|
||||
pub TestLaneId: LaneId = TEST_LANE_ID;
|
||||
pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(
|
||||
TEST_LANE_ID,
|
||||
@@ -895,6 +985,14 @@ mod tests {
|
||||
|
||||
bp_runtime::generate_static_str_provider!(TestExtension);
|
||||
|
||||
type TestMessagesExtensionProvider = RefundBridgedMessages<
|
||||
TestRuntime,
|
||||
RefundableMessagesLane<(), TestLaneId>,
|
||||
ActualFeeRefund<TestRuntime>,
|
||||
ConstU64<1>,
|
||||
StrTestExtension,
|
||||
>;
|
||||
type TestMessagesExtension = RefundSignedExtensionAdapter<TestMessagesExtensionProvider>;
|
||||
type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages<
|
||||
TestRuntime,
|
||||
(),
|
||||
@@ -906,7 +1004,7 @@ mod tests {
|
||||
type TestGrandpaExtension = RefundSignedExtensionAdapter<TestGrandpaExtensionProvider>;
|
||||
type TestExtensionProvider = RefundBridgedParachainMessages<
|
||||
TestRuntime,
|
||||
DefaultRefundableParachainId<(), TestParachain>,
|
||||
RefundableParachain<(), BridgedUnderlyingParachain>,
|
||||
RefundableMessagesLane<(), TestLaneId>,
|
||||
ActualFeeRefund<TestRuntime>,
|
||||
ConstU64<1>,
|
||||
@@ -930,7 +1028,7 @@ mod tests {
|
||||
TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get())
|
||||
}
|
||||
|
||||
fn relayer_account_at_this_chain() -> ThisChainAccountId {
|
||||
pub fn relayer_account_at_this_chain() -> ThisChainAccountId {
|
||||
0
|
||||
}
|
||||
|
||||
@@ -938,7 +1036,7 @@ mod tests {
|
||||
0
|
||||
}
|
||||
|
||||
fn initialize_environment(
|
||||
pub fn initialize_environment(
|
||||
best_relay_header_number: RelayBlockNumber,
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
@@ -949,8 +1047,12 @@ mod tests {
|
||||
StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(),
|
||||
);
|
||||
pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(best_relay_header);
|
||||
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
|
||||
best_relay_header.hash(),
|
||||
bp_test_utils::test_header::<BridgedChainHeader>(0).build(),
|
||||
);
|
||||
|
||||
let para_id = ParaId(TestParachain::get());
|
||||
let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID);
|
||||
let para_info = ParaInfo {
|
||||
best_head_hash: BestParaHeadHash {
|
||||
at_relay_block_number: parachain_head_at_relay_header_number,
|
||||
@@ -994,7 +1096,7 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall {
|
||||
pub fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall {
|
||||
let relay_header = BridgedChainHeader::new(
|
||||
relay_header_number,
|
||||
Default::default(),
|
||||
@@ -1008,6 +1110,7 @@ mod tests {
|
||||
finality_target: Box::new(relay_header),
|
||||
justification: relay_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
is_free_execution_expected: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1017,13 +1120,27 @@ mod tests {
|
||||
RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads {
|
||||
at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()),
|
||||
parachains: vec![(
|
||||
ParaId(TestParachain::get()),
|
||||
ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
[parachain_head_at_relay_header_number as u8; 32].into(),
|
||||
)],
|
||||
parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn submit_parachain_head_call_ex(
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
) -> RuntimeCall {
|
||||
RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads_ex {
|
||||
at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()),
|
||||
parachains: vec![(
|
||||
ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
[parachain_head_at_relay_header_number as u8; 32].into(),
|
||||
)],
|
||||
parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
|
||||
is_free_execution_expected: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall {
|
||||
RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof {
|
||||
relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(),
|
||||
@@ -1151,7 +1268,7 @@ mod tests {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
submit_parachain_head_call(parachain_head_at_relay_header_number),
|
||||
submit_parachain_head_call_ex(parachain_head_at_relay_header_number),
|
||||
message_delivery_call(best_message),
|
||||
],
|
||||
})
|
||||
@@ -1179,7 +1296,7 @@ mod tests {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
submit_parachain_head_call(parachain_head_at_relay_header_number),
|
||||
submit_parachain_head_call_ex(parachain_head_at_relay_header_number),
|
||||
message_confirmation_call(best_message),
|
||||
],
|
||||
})
|
||||
@@ -1194,11 +1311,14 @@ mod tests {
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
SubmitParachainHeadsInfo {
|
||||
at_relay_block_number: 200,
|
||||
para_id: ParaId(TestParachain::get()),
|
||||
at_relay_block: HeaderId(200, [0u8; 32].into()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [200u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo {
|
||||
base: BaseMessagesProofInfo {
|
||||
@@ -1231,11 +1351,14 @@ mod tests {
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
SubmitParachainHeadsInfo {
|
||||
at_relay_block_number: 200,
|
||||
para_id: ParaId(TestParachain::get()),
|
||||
at_relay_block: HeaderId(200, [0u8; 32].into()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [200u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo(
|
||||
BaseMessagesProofInfo {
|
||||
@@ -1264,6 +1387,8 @@ mod tests {
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo {
|
||||
base: BaseMessagesProofInfo {
|
||||
@@ -1296,6 +1421,8 @@ mod tests {
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo(
|
||||
BaseMessagesProofInfo {
|
||||
@@ -1320,9 +1447,10 @@ mod tests {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::ParachainFinalityAndMsgs(
|
||||
SubmitParachainHeadsInfo {
|
||||
at_relay_block_number: 200,
|
||||
para_id: ParaId(TestParachain::get()),
|
||||
at_relay_block: HeaderId(200, [0u8; 32].into()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [200u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo {
|
||||
base: BaseMessagesProofInfo {
|
||||
@@ -1344,9 +1472,10 @@ mod tests {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::ParachainFinalityAndMsgs(
|
||||
SubmitParachainHeadsInfo {
|
||||
at_relay_block_number: 200,
|
||||
para_id: ParaId(TestParachain::get()),
|
||||
at_relay_block: HeaderId(200, [0u8; 32].into()),
|
||||
para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
|
||||
para_head_hash: [200u8; 32].into(),
|
||||
is_free_execution_expected: false,
|
||||
},
|
||||
MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo(
|
||||
BaseMessagesProofInfo {
|
||||
@@ -1421,8 +1550,14 @@ mod tests {
|
||||
extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0)
|
||||
}
|
||||
|
||||
fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity {
|
||||
run_validate(call).map(|mut tx| {
|
||||
fn run_messages_validate(call: RuntimeCall) -> TransactionValidity {
|
||||
let extension: TestMessagesExtension =
|
||||
RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData));
|
||||
extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0)
|
||||
}
|
||||
|
||||
fn ignore_priority(tx: TransactionValidity) -> TransactionValidity {
|
||||
tx.map(|mut tx| {
|
||||
tx.priority = 0;
|
||||
tx
|
||||
})
|
||||
@@ -1444,6 +1579,14 @@ mod tests {
|
||||
extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0)
|
||||
}
|
||||
|
||||
fn run_messages_pre_dispatch(
|
||||
call: RuntimeCall,
|
||||
) -> Result<Option<PreDispatchData<ThisChainAccountId>>, TransactionValidityError> {
|
||||
let extension: TestMessagesExtension =
|
||||
RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData));
|
||||
extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0)
|
||||
}
|
||||
|
||||
fn dispatch_info() -> DispatchInfo {
|
||||
DispatchInfo {
|
||||
weight: Weight::from_parts(
|
||||
@@ -1502,40 +1645,48 @@ mod tests {
|
||||
Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get());
|
||||
|
||||
// message delivery is failing
|
||||
assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),);
|
||||
assert_eq!(
|
||||
run_validate(parachain_finality_and_delivery_batch_call(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
let fns = [run_validate, run_grandpa_validate, run_messages_validate];
|
||||
for f in fns {
|
||||
assert_eq!(f(message_delivery_call(200)), Ok(Default::default()),);
|
||||
assert_eq!(
|
||||
f(parachain_finality_and_delivery_batch_call(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
f(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
f(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
}
|
||||
|
||||
// message confirmation validation is passing
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(message_confirmation_call(200)),
|
||||
ignore_priority(run_validate(message_confirmation_call(200))),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call(
|
||||
ignore_priority(run_messages_validate(message_confirmation_call(200))),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call(
|
||||
200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call(
|
||||
ignore_priority(run_validate(all_finality_and_confirmation_batch_call(
|
||||
200, 200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex(
|
||||
ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
});
|
||||
@@ -1549,25 +1700,28 @@ mod tests {
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
let priority_of_100_messages_delivery =
|
||||
run_validate(message_delivery_call(200)).unwrap().priority;
|
||||
let priority_of_200_messages_delivery =
|
||||
run_validate(message_delivery_call(300)).unwrap().priority;
|
||||
assert!(
|
||||
priority_of_200_messages_delivery > priority_of_100_messages_delivery,
|
||||
"Invalid priorities: {} for 200 messages vs {} for 100 messages",
|
||||
priority_of_200_messages_delivery,
|
||||
priority_of_100_messages_delivery,
|
||||
);
|
||||
let fns = [run_validate, run_grandpa_validate, run_messages_validate];
|
||||
for f in fns {
|
||||
let priority_of_100_messages_delivery =
|
||||
f(message_delivery_call(200)).unwrap().priority;
|
||||
let priority_of_200_messages_delivery =
|
||||
f(message_delivery_call(300)).unwrap().priority;
|
||||
assert!(
|
||||
priority_of_200_messages_delivery > priority_of_100_messages_delivery,
|
||||
"Invalid priorities: {} for 200 messages vs {} for 100 messages",
|
||||
priority_of_200_messages_delivery,
|
||||
priority_of_100_messages_delivery,
|
||||
);
|
||||
|
||||
let priority_of_100_messages_confirmation =
|
||||
run_validate(message_confirmation_call(200)).unwrap().priority;
|
||||
let priority_of_200_messages_confirmation =
|
||||
run_validate(message_confirmation_call(300)).unwrap().priority;
|
||||
assert_eq!(
|
||||
priority_of_100_messages_confirmation,
|
||||
priority_of_200_messages_confirmation
|
||||
);
|
||||
let priority_of_100_messages_confirmation =
|
||||
f(message_confirmation_call(200)).unwrap().priority;
|
||||
let priority_of_200_messages_confirmation =
|
||||
f(message_confirmation_call(300)).unwrap().priority;
|
||||
assert_eq!(
|
||||
priority_of_100_messages_confirmation,
|
||||
priority_of_200_messages_confirmation
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1579,23 +1733,24 @@ mod tests {
|
||||
BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000)
|
||||
.unwrap();
|
||||
|
||||
let priority_of_max_messages_delivery = run_validate(message_delivery_call(
|
||||
100 + MaxUnconfirmedMessagesAtInboundLane::get(),
|
||||
))
|
||||
.unwrap()
|
||||
.priority;
|
||||
let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call(
|
||||
100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1,
|
||||
))
|
||||
.unwrap()
|
||||
.priority;
|
||||
let fns = [run_validate, run_grandpa_validate, run_messages_validate];
|
||||
for f in fns {
|
||||
let priority_of_max_messages_delivery =
|
||||
f(message_delivery_call(100 + MaxUnconfirmedMessagesAtInboundLane::get()))
|
||||
.unwrap()
|
||||
.priority;
|
||||
let priority_of_more_than_max_messages_delivery =
|
||||
f(message_delivery_call(100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1))
|
||||
.unwrap()
|
||||
.priority;
|
||||
|
||||
assert!(
|
||||
priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery,
|
||||
"Invalid priorities: {} for MAX messages vs {} for MAX+1 messages",
|
||||
priority_of_max_messages_delivery,
|
||||
priority_of_more_than_max_messages_delivery,
|
||||
);
|
||||
assert!(
|
||||
priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery,
|
||||
"Invalid priorities: {} for MAX messages vs {} for MAX+1 messages",
|
||||
priority_of_max_messages_delivery,
|
||||
priority_of_more_than_max_messages_delivery,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1605,45 +1760,54 @@ mod tests {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(message_delivery_call(200)),
|
||||
ignore_priority(run_validate(message_delivery_call(200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(message_confirmation_call(200)),
|
||||
ignore_priority(run_validate(message_confirmation_call(200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(parachain_finality_and_delivery_batch_call(200, 200)),
|
||||
ignore_priority(run_messages_validate(message_delivery_call(200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call(
|
||||
ignore_priority(run_messages_validate(message_confirmation_call(200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ignore_priority(run_validate(parachain_finality_and_delivery_batch_call(200, 200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call(
|
||||
200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
ignore_priority(run_validate(all_finality_and_delivery_batch_call(200, 200, 200))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_delivery_batch_call_ex(
|
||||
ignore_priority(run_validate(all_finality_and_delivery_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call(
|
||||
ignore_priority(run_validate(all_finality_and_confirmation_batch_call(
|
||||
200, 200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex(
|
||||
ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
))),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
});
|
||||
@@ -1933,8 +2097,11 @@ mod tests {
|
||||
RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads {
|
||||
at_relay_block: (100, RelayBlockHash::default()),
|
||||
parachains: vec![
|
||||
(ParaId(TestParachain::get()), [1u8; 32].into()),
|
||||
(ParaId(TestParachain::get() + 1), [1u8; 32].into()),
|
||||
(ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), [1u8; 32].into()),
|
||||
(
|
||||
ParaId(BridgedUnderlyingParachain::PARACHAIN_ID + 1),
|
||||
[1u8; 32].into(),
|
||||
),
|
||||
],
|
||||
parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
|
||||
}),
|
||||
@@ -2318,6 +2485,148 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn messages_ext_only_parses_standalone_transactions() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
// relay + parachain + message delivery calls batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_delivery_batch_call(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_delivery_batch_call_ex(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// relay + parachain + message confirmation calls batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_confirmation_batch_call(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_confirmation_batch_call_ex(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// parachain + message delivery call batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
¶chain_finality_and_delivery_batch_call(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// parachain + message confirmation call batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
¶chain_finality_and_confirmation_batch_call(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// relay + message delivery call batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_delivery_batch_call(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_delivery_batch_call_ex(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// relay + message confirmation call batch is ignored
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_confirmation_batch_call(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_confirmation_batch_call_ex(200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// message delivery call batch is accepted
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&message_delivery_call(200)
|
||||
),
|
||||
Ok(Some(delivery_pre_dispatch_data().call_info)),
|
||||
);
|
||||
|
||||
// message confirmation call batch is accepted
|
||||
assert_eq!(
|
||||
TestMessagesExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&message_confirmation_call(200)
|
||||
),
|
||||
Ok(Some(confirmation_pre_dispatch_data().call_info)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn messages_ext_rejects_calls_with_obsolete_messages() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
assert_eq!(
|
||||
run_messages_pre_dispatch(message_delivery_call(100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_messages_pre_dispatch(message_confirmation_call(100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_messages_validate(message_delivery_call(100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_messages_validate(message_confirmation_call(100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn messages_ext_accepts_calls_with_new_messages() {
|
||||
run_test(|| {
|
||||
initialize_environment(100, 100, 100);
|
||||
|
||||
assert_eq!(
|
||||
run_messages_pre_dispatch(message_delivery_call(200)),
|
||||
Ok(Some(delivery_pre_dispatch_data())),
|
||||
);
|
||||
assert_eq!(
|
||||
run_messages_pre_dispatch(message_confirmation_call(200)),
|
||||
Ok(Some(confirmation_pre_dispatch_data())),
|
||||
);
|
||||
|
||||
assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),);
|
||||
assert_eq!(
|
||||
run_messages_validate(message_confirmation_call(200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grandpa_ext_only_parses_valid_batches() {
|
||||
run_test(|| {
|
||||
|
||||
@@ -183,7 +183,8 @@ impl pallet_transaction_payment::Config for TestRuntime {
|
||||
impl pallet_bridge_grandpa::Config for TestRuntime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BridgedChain = BridgedUnderlyingChain;
|
||||
type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>;
|
||||
type MaxFreeHeadersPerBlock = ConstU32<4>;
|
||||
type FreeHeadersInterval = ConstU32<1_024>;
|
||||
type HeadersToKeep = ConstU32<8>;
|
||||
type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<TestRuntime>;
|
||||
}
|
||||
@@ -406,6 +407,7 @@ impl Chain for BridgedUnderlyingParachain {
|
||||
|
||||
impl Parachain for BridgedUnderlyingParachain {
|
||||
const PARACHAIN_ID: u32 = 42;
|
||||
const MAX_HEADER_SIZE: u32 = 1_024;
|
||||
}
|
||||
|
||||
/// The other, bridged chain, used in tests.
|
||||
|
||||
Reference in New Issue
Block a user