From ec0ce632650b3e4a4d67cbabf074da0b1b3fff74 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 28 Sep 2023 09:40:36 +0300 Subject: [PATCH] Move refund extension to relayers pallet (#2584) * moved all call-related definitions to primitives * moving refund extension to relayers pallet * cleaning up * fix compilation + spelling --- Cargo.lock | 14 +- .../extensions/check_obsolete_extension.rs | 606 +++--- .../bin/runtime-common/src/extensions/mod.rs | 4 +- bridges/bin/runtime-common/src/mock.rs | 1 + .../src/parachains_benchmarking.rs | 8 +- bridges/modules/grandpa/src/call_ext.rs | 3 +- bridges/modules/parachains/src/lib.rs | 10 +- bridges/modules/relayers/Cargo.toml | 18 + .../relayers/src/extension/grandpa_adapter.rs | 178 ++ .../relayers/src/extension/mod.rs} | 1680 ++++++----------- .../src/extension/parachain_adapter.rs | 185 ++ .../relayers/src/extension/priority.rs | 202 ++ bridges/modules/relayers/src/lib.rs | 21 +- bridges/modules/relayers/src/mock.rs | 286 ++- .../modules/relayers/src/payment_adapter.rs | 8 +- bridges/modules/relayers/src/stake_adapter.rs | 2 +- .../primitives/header-chain/src/call_info.rs | 15 +- bridges/primitives/messages/src/call_info.rs | 8 + bridges/primitives/parachains/src/lib.rs | 13 +- bridges/primitives/relayers/Cargo.toml | 10 +- bridges/primitives/relayers/src/extension.rs | 193 ++ bridges/primitives/relayers/src/lib.rs | 5 + bridges/relays/lib-substrate-relay/Cargo.toml | 1 - .../lib-substrate-relay/src/cli/bridge.rs | 2 +- .../parachain_to_parachain.rs | 2 +- .../relay_to_parachain.rs | 2 +- .../src/on_demand/parachains.rs | 2 +- .../lib-substrate-relay/src/parachains/mod.rs | 6 +- 28 files changed, 1950 insertions(+), 1535 deletions(-) create mode 100644 bridges/modules/relayers/src/extension/grandpa_adapter.rs rename bridges/{bin/runtime-common/src/extensions/refund_relayer_extension.rs => modules/relayers/src/extension/mod.rs} (55%) create mode 100644 bridges/modules/relayers/src/extension/parachain_adapter.rs create mode 100644 bridges/modules/relayers/src/extension/priority.rs create mode 100644 bridges/primitives/relayers/src/extension.rs diff --git a/Cargo.lock b/Cargo.lock index 757e684313ccf..d7e6e47a32f1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1846,11 +1846,15 @@ dependencies = [ name = "bp-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", "bp-runtime", "frame-support", + "frame-system", "hex", "hex-literal", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-runtime", @@ -9894,18 +9898,27 @@ dependencies = [ name = "pallet-bridge-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", + "bp-polkadot-core", "bp-relayers", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", "log", "pallet-balances", + "pallet-bridge-grandpa", "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-arithmetic", + "sp-core", "sp-io", "sp-runtime", "sp-std 14.0.0", @@ -20741,7 +20754,6 @@ dependencies = [ "bp-polkadot-core", "bp-relayers", "bp-runtime", - "bridge-runtime-common", "equivocation-detector", "finality-grandpa", "finality-relay", diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs index 13d1021a3008a..52e79086bc7f5 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs @@ -18,20 +18,10 @@ //! obsolete (duplicated) data or do not pass some additional pallet-specific //! checks. -use crate::extensions::refund_relayer_extension::RefundableParachainId; -use bp_parachains::SubmitParachainHeadsInfo; -use bp_relayers::ExplicitOrAccountParams; -use bp_runtime::Parachain; -use pallet_bridge_grandpa::{ - BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, -}; +use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; use pallet_bridge_messages::CallSubType as MessagesCallSubType; -use pallet_bridge_parachains::{CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper}; -use pallet_bridge_relayers::Pallet as RelayersPallet; -use sp_runtime::{ - traits::{Get, PhantomData, UniqueSaturatedInto}, - transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder}, -}; +use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; +use sp_runtime::transaction_validity::{TransactionValidity, ValidTransactionBuilder}; /// A duplication of the `FilterCall` trait. /// @@ -49,128 +39,129 @@ pub trait BridgeRuntimeFilterCall { } } -/// 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( - PhantomData<(T, I, Priority, SlashAccount)>, -); - -impl, SlashAccount: Get> - BridgeRuntimeFilterCall - for CheckAndBoostBridgeGrandpaTransactions -where - T: pallet_bridge_relayers::Config + pallet_bridge_grandpa::Config, - T::RuntimeCall: GrandpaCallSubType, -{ - // bridged header number, bundled in transaction - type ToPostDispatch = Option>; - - fn validate( - who: &T::AccountId, - call: &T::RuntimeCall, - ) -> (Self::ToPostDispatch, TransactionValidity) { - match GrandpaCallSubType::::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::(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::::was_successful(bundled_block_number); - - if !has_failed { - return - } - - // let's slash registered relayer - RelayersPallet::::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( - PhantomData<(T, RefPara, Priority, SlashAccount)>, -); - -impl, SlashAccount: Get> - BridgeRuntimeFilterCall - for CheckAndBoostBridgeParachainsTransactions -where - T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, - RefPara: RefundableParachainId, - T::RuntimeCall: ParachainsCallSubtype, -{ - // bridged header number, bundled in transaction - type ToPostDispatch = Option; - - fn validate( - who: &T::AccountId, - call: &T::RuntimeCall, - ) -> (Self::ToPostDispatch, TransactionValidity) { - match ParachainsCallSubtype::::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::(&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::::was_successful(&update); - - if !has_failed { - return - } - - // let's slash registered relayer - RelayersPallet::::slash_and_deregister( - relayer, - ExplicitOrAccountParams::Explicit(SlashAccount::get()), - ); - } -} +// TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 +// /// 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( +// PhantomData<(T, I, Priority, SlashAccount)>, +// ); +// +// impl, SlashAccount: Get> +// BridgeRuntimeFilterCall +// for CheckAndBoostBridgeGrandpaTransactions +// where +// T: pallet_bridge_relayers::Config + pallet_bridge_grandpa::Config, +// T::RuntimeCall: GrandpaCallSubType, +// { +// // bridged header number, bundled in transaction +// type ToPostDispatch = Option>; +// +// fn validate( +// who: &T::AccountId, +// call: &T::RuntimeCall, +// ) -> (Self::ToPostDispatch, TransactionValidity) { +// match GrandpaCallSubType::::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::(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::::was_successful(bundled_block_number); +// +// if !has_failed { +// return +// } +// +// // let's slash registered relayer +// RelayersPallet::::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( +// PhantomData<(T, RefPara, Priority, SlashAccount)>, +// ); +// +// impl, SlashAccount: Get> +// BridgeRuntimeFilterCall +// for CheckAndBoostBridgeParachainsTransactions +// where +// T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, +// RefPara: RefundableParachainId, +// T::RuntimeCall: ParachainsCallSubtype, +// { +// // bridged header number, bundled in transaction +// type ToPostDispatch = Option; +// +// fn validate( +// who: &T::AccountId, +// call: &T::RuntimeCall, +// ) -> (Self::ToPostDispatch, TransactionValidity) { +// match ParachainsCallSubtype::::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::(&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::::was_successful(&update); +// +// if !has_failed { +// return +// } +// +// // let's slash registered relayer +// RelayersPallet::::slash_and_deregister( +// relayer, +// ExplicitOrAccountParams::Explicit(SlashAccount::get()), +// ); +// } +// } impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet @@ -219,24 +210,25 @@ where } } +// TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 /// Computes priority boost that improved known header by `improved_by` -fn compute_priority_boost( - relayer: &T::AccountId, - improved_by: N, -) -> TransactionPriority -where - T: pallet_bridge_relayers::Config, - N: UniqueSaturatedInto, - Priority: Get, -{ - // we only boost priority if relayer has staked required balance - let is_relayer_registration_active = RelayersPallet::::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) -} +// fn compute_priority_boost( +// relayer: &T::AccountId, +// improved_by: N, +// ) -> TransactionPriority +// where +// T: pallet_bridge_relayers::Config, +// N: UniqueSaturatedInto, +// Priority: Get, +// { +// // we only boost priority if relayer has staked required balance +// let is_relayer_registration_active = RelayersPallet::::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. /// @@ -354,21 +346,10 @@ 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 crate::mock::*; use frame_support::{assert_err, assert_ok}; use sp_runtime::{ - traits::{ConstU64, SignedExtension}, + traits::SignedExtension, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; @@ -527,151 +508,152 @@ mod tests { pub SlashDestination: ThisChainAccountId = 42; } - type BridgeGrandpaWrapper = - CheckAndBoostBridgeGrandpaTransactions, 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())); - }) - } + // TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 + // type BridgeGrandpaWrapper = + // CheckAndBoostBridgeGrandpaTransactions, 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())); + // }) + // } } diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs index 3f1b506aaae3e..74bba5445bcff 100644 --- a/bridges/bin/runtime-common/src/extensions/mod.rs +++ b/bridges/bin/runtime-common/src/extensions/mod.rs @@ -17,5 +17,5 @@ //! Bridge-specific transaction extensions. pub mod check_obsolete_extension; -pub mod priority_calculator; -pub mod refund_relayer_extension; +// TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102, migrate to the `modules/relayers/src/extension` +// pub mod priority_calculator; diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index e5a6df0c783a1..54d4f2105d705 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -84,6 +84,7 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< >; /// Message lane used in tests. +#[allow(unused)] pub fn test_lane_id() -> LaneId { LaneId::new(1, 2) } diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs index e64b6fc22392b..a94305bafd375 100644 --- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs @@ -20,13 +20,15 @@ use crate::messages_benchmarking::insert_header_to_grandpa_pallet; -use bp_parachains::parachain_head_storage_key_at_source; +use bp_parachains::{ + parachain_head_storage_key_at_source, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, +}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{grow_storage_value, Chain, UnverifiedStorageProof, UnverifiedStorageProofParams}; use codec::Encode; -use frame_support::{sp_runtime::StateVersion, traits::Get}; +use frame_support::traits::Get; use pallet_bridge_grandpa::BridgedChain; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use sp_runtime::StateVersion; use sp_std::prelude::*; use sp_trie::{LayoutV0, LayoutV1, MemoryDB, TrieConfiguration, TrieDBMutBuilder, TrieMut}; diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index 3b3354955cd80..22cf51793e481 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -35,10 +35,11 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, RuntimeDebug, SaturatedConversion, }; +use sp_std::fmt::Debug; /// Verified `SubmitFinalityProofInfo`. #[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct VerifiedSubmitFinalityProofInfo { +pub struct VerifiedSubmitFinalityProofInfo { /// Base call information. pub base: SubmitFinalityProofInfo, /// A difference between bundled bridged header and best bridged header known to us diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 4a7193243944f..20756a5e64b68 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -29,7 +29,8 @@ pub use weights_ext::WeightInfoExt; use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_parachains::{ - parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData, SubmitParachainHeadsInfo, + parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData, RelayBlockHash, + RelayBlockHasher, RelayBlockNumber, SubmitParachainHeadsInfo, }; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain}; @@ -61,13 +62,6 @@ mod mock; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-parachains"; -/// Block hash of the bridged relay chain. -pub type RelayBlockHash = bp_polkadot_core::Hash; -/// Block number of the bridged relay chain. -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -/// Hasher of the bridged relay chain. -pub type RelayBlockHasher = bp_polkadot_core::Hasher; - /// Artifacts of the parachains head update. struct UpdateParachainHeadArtifacts { /// New best head of the parachain. diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 08e1438d4f194..142a200dad9c6 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -17,29 +17,39 @@ scale-info = { version = "2.11.1", default-features = false, features = ["derive # Bridge dependencies +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } bp-relayers = { path = "../../primitives/relayers", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-grandpa = { path = "../grandpa", default-features = false } pallet-bridge-messages = { path = "../messages", default-features = false } +pallet-bridge-parachains = { path = "../parachains", default-features = false } # Substrate Dependencies frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-support = { path = "../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../substrate/frame/system", default-features = false } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } sp-arithmetic = { path = "../../../substrate/primitives/arithmetic", default-features = false } sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } sp-std = { path = "../../../substrate/primitives/std", default-features = false } [dev-dependencies] bp-runtime = { path = "../../primitives/runtime" } +bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-test-utils = { path = "../../primitives/test-utils" } pallet-balances = { path = "../../../substrate/frame/balances" } sp-io = { path = "../../../substrate/primitives/io" } sp-runtime = { path = "../../../substrate/primitives/runtime" } +pallet-utility = { path = "../../../substrate/frame/utility" } +sp-core = { path = "../../../substrate/primitives/core" } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", "bp-relayers/std", "bp-runtime/std", @@ -48,7 +58,10 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-transaction-payment/std", "scale-info/std", "sp-arithmetic/std", "sp-runtime/std", @@ -59,13 +72,18 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", + "pallet-bridge-grandpa/try-runtime", "pallet-bridge-messages/try-runtime", + "pallet-bridge-parachains/try-runtime", "sp-runtime/try-runtime", ] +integrity-test = [] diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs new file mode 100644 index 0000000000000..f784d0e9ea9a4 --- /dev/null +++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs @@ -0,0 +1,178 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote GRANDPA chain. + +use crate::{ + extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Chain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, + SubmitFinalityProofHelper, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, + Saturating, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote +/// chains that are using GRANDPA finality. +pub struct WithGrandpaChainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeGrandpaConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-grandpa`, tracked by this extension + BridgeGrandpaPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeGrandpaPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig + for WithGrandpaChainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig> + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + GI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BatchCallUnpacker = BCU; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = Self::BatchCallUnpacker::unpack(call, 2); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, msgs_call) { + (2, Some(relay_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), + (1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the GRANDPA chain state update call, verify that it +/// has been successful. +/// +/// Only returns false when GRANDPA chain state update call has failed. +pub(crate) fn verify_submit_finality_proof_succeeded( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + GI: 'static, + C::Runtime: BridgeGrandpaConfig, + >::BridgedChain: + Chain, +{ + let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true }; + + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid GRANDPA chain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + 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 + call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight); + call_data.extra_size.saturating_accrue(finality_proof_info.extra_size); + + true +} diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/modules/relayers/src/extension/mod.rs similarity index 55% rename from bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs rename to bridges/modules/relayers/src/extension/mod.rs index 70238225df2ae..865e8a2a8c62a 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -14,208 +14,74 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use bp_header_chain::SubmitFinalityProofInfo; -use bp_messages::{ChainWithMessages, LaneId, MessageNonce, MessagesCallInfo}; -use bp_parachains::SubmitParachainHeadsInfo; -use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; +//! Signed extension, built around `pallet-bridge-relayers`. It is able to: +//! +//! - refund the cost of successful message delivery and confirmation transactions to the submitter +//! by registering corresponding reward in the pallet; +//! +//! - bump priority of messages delivery and confirmation transactions, signed by the registered +//! relayers. + +use crate::{Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt, LOG_TARGET}; + +use bp_messages::{ChainWithMessages, MessageNonce}; +use bp_relayers::{ + ExplicitOrAccountParams, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RewardsAccountOwner, RewardsAccountParams, +}; +use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; +use codec::{Decode, Encode}; use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, + dispatch::{DispatchInfo, PostDispatchInfo}, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper}; -use pallet_bridge_messages::{ - CallHelper as MessagesCallHelper, CallSubType as MessagesCallSubType, Config as MessagesConfig, -}; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig}; +use pallet_transaction_payment::{ + Config as TransactionPaymentConfig, OnChargeTransaction, Pallet as TransactionPaymentPallet, }; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, -}; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchResult, FixedPointOperand, RuntimeDebug, + DispatchResult, RuntimeDebug, }; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance: 'static; - /// The parachain Id. - type BridgedChain: Parachain; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Instance: 'static, - Para: Parachain, -{ - type Instance = Instance; - type BridgedChain = Para; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} +use sp_std::{fmt::Debug, marker::PhantomData}; -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Runtime, Instance, Lane)>); +pub use grandpa_adapter::WithGrandpaChainExtensionConfig; +pub use parachain_adapter::WithParachainExtensionConfig; +pub use priority::*; -impl RefundableMessagesLaneId - for RefundableMessagesLane -where - Runtime: MessagesConfig, - Instance: 'static, - Lane: Get, -{ - type Instance = Instance; - type Id = Lane; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} - -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); - -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} +mod grandpa_adapter; +mod parachain_adapter; +mod priority; /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. #[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { +pub struct PreDispatchData { /// Transaction submitter (relayer) account. relayer: AccountId, /// Type of the call. - call_info: CallInfo, -} - -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), + call_info: ExtensionCallInfo, } -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - +impl + PreDispatchData +{ /// Returns mutable reference to pre-dispatch `finality_target` sent to the /// `SubmitFinalityProof` call. #[cfg(test)] - fn submit_finality_proof_info_mut( + pub fn submit_finality_proof_info_mut( &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), + ) -> Option<&mut bp_header_chain::SubmitFinalityProofInfo> { + match self.call_info { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), + ExtensionCallInfo::RelayFinalityAndMsgs(ref mut info, _) => Some(info), _ => None, } } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } } /// The actions on relayer account that need to be performed because of his actions. @@ -229,78 +95,93 @@ pub enum RelayerAccountAction { Slash(AccountId, RewardsAccountParams), } -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo +/// A signed extension, built around `pallet-bridge-relayers`. +/// +/// It may be incorporated into runtime to refund relayers for submitting correct +/// message delivery and confirmation transactions, optionally batched with required +/// finality proofs. +#[derive( + DefaultNoBound, + CloneNoBound, + Decode, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(Runtime, Config))] +pub struct BridgeRelayersSignedExtension(PhantomData<(Runtime, Config)>); + +impl BridgeRelayersSignedExtension +where + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - /// This chain runtime. - type Runtime: MessagesConfig<::Instance> - + RelayersConfig; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// 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, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool; + /// Returns number of bundled messages `Some(_)`, if the given call info is a: + /// + /// - message delivery transaction; + /// + /// - with reasonable bundled messages that may be accepted by the messages pallet. + /// + /// This function is used to check whether the transaction priority should be + /// virtually boosted. The relayer registration (we only boost priority for registered + /// relayer transactions) must be checked outside. + fn bundled_messages_for_priority_boost( + call_info: Option<&ExtensionCallInfo>, + ) -> Option { + // we only boost priority of message delivery transactions + let parsed_call = match call_info { + Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, + _ => return None, + }; + + // compute total number of messages in transaction + let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); + + // a quick check to avoid invalid high-priority transactions + let max_unconfirmed_messages_in_confirmation_tx = >::BridgedChain + ::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { + return None + } + + Some(bundled_messages) + } /// Given post-dispatch information, analyze the outcome of relayer call and return /// actions that need to be performed on relayer account. fn analyze_call_result( - pre: Option>>>, + pre: Option>>, info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - + ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { Some(Some(pre)) => (pre.relayer, pre.call_info), _ => return RelayerAccountAction::None, }; - // now we know that the relayer either needs to be rewarded, or slashed + // now we know that the call is supported and we may need to reward or slash relayer // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChain::ID, - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); + let lane_id = call_info.messages_call_info().lane_id(); + let reward_account_params = RewardsAccountParams::new( + lane_id, + >::BridgedChain::ID, + if call_info.is_receive_messages_proof_call() { + RewardsAccountOwner::ThisChain + } else { + RewardsAccountOwner::BridgedChain + }, + ); // prepare return value for the case if the call has failed or it has not caused // expected side effects (e.g. not all messages have been accepted) @@ -323,37 +204,19 @@ pub trait RefundSignedExtension: // We don't refund anything if the transaction has failed. if let Err(e) = result { log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages transaction: {:?}", + Self::IDENTIFIER, + lane_id, relayer, e, ); return slash_relayer_if_delivery_result } - // 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(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional checks - if !Self::additional_call_result_check( - &relayer, - &call_info, - &mut extra_weight, - &mut extra_size, - ) { + // check whether the call has succeeded + let mut call_data = ExtensionCallData::default(); + if !C::check_call_result(&call_info, &mut call_data, &relayer) { return slash_relayer_if_delivery_result } @@ -365,81 +228,55 @@ pub trait RefundSignedExtension: let tip = Zero::zero(); // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); + let post_info_len = len.saturating_sub(call_data.extra_size as usize); + let mut post_info_weight = post_info + .actual_weight + .unwrap_or(info.weight) + .saturating_sub(call_data.extra_weight); // let's also replace the weight of slashing relayer with the weight of rewarding relayer if call_info.is_receive_messages_proof_call() { post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), + ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), ); } // compute the relayer refund let mut post_info = *post_info; post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); + let refund = Self::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer RelayerAccountAction::Reward(relayer, reward_account_params, refund) } - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) + /// Compute refund for the successful relayer transaction + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: R::Reward, + ) -> R::Reward { + TransactionPaymentPallet::::compute_actual_fee(len as _, info, post_info, tip) } } -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T); - -impl SignedExtension for RefundSignedExtensionAdapter +impl SignedExtension for BridgeRelayersSignedExtension where - CallOf: Dispatchable - + MessagesCallSubType::Instance>, + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; + const IDENTIFIER: &'static str = C::IdProvider::STR; + type AccountId = R::AccountId; + type Call = R::RuntimeCall; type AdditionalSigned = (); - type Pre = Option>>; + type Pre = Option>; fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) @@ -457,33 +294,33 @@ where // we're not calling `validate` from `pre_dispatch` directly because of performance // reasons, so if you're adding some code that may fail here, please check if it needs // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; // the following code just plays with transaction priority and never returns an error // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { + let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) + { Some(bundled_messages) => bundled_messages, None => return Ok(Default::default()), }; // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { + if !RelayersPallet::::is_registration_active(who) { return Ok(Default::default()) } // compute priority boost - let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::< - T::Priority, - >(bundled_messages); + let priority_boost = + priority::compute_priority_boost::(bundled_messages); let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ + target: LOG_TARGET, + "{}.{:?}: has boosted priority of message delivery transaction \ of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, - ::Id::get(), + parsed_call.as_ref().map(|p| p.messages_call_info().lane_id()), who, bundled_messages, priority_boost, @@ -500,14 +337,14 @@ where _len: usize, ) -> Result { // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; Ok(parsed_call.map(|call_info| { log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + target: LOG_TARGET, + "{}.{:?}: parsed bridge transaction in pre-dispatch: {:?}", Self::IDENTIFIER, - ::Id::get(), + call_info.messages_call_info().lane_id(), call_info, ); PreDispatchData { relayer: who.clone(), call_info } @@ -521,28 +358,28 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); + let lane_id = pre + .as_ref() + .and_then(|p| p.as_ref()) + .map(|p| p.call_info.messages_call_info().lane_id()); + let call_result = Self::analyze_call_result(pre, info, post_info, len, result); match call_result { RelayerAccountAction::None => (), RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); + RelayersPallet::::register_relayer_reward(reward_account, &relayer, reward); log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", + target: LOG_TARGET, + "{}.{:?}: has registered reward: {:?} for {:?}", Self::IDENTIFIER, - ::Id::get(), + lane_id, reward, relayer, ); }, RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister( + RelayersPallet::::slash_and_deregister( &relayer, ExplicitOrAccountParams::Params(slash_account), ), @@ -552,411 +389,61 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // 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 RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - RefundBridgedGrandpaMessages< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .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()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - 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 { - // 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::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::BridgedChain::PARACHAIN_ID, - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // 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 RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - 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 { - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::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, - ::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( - 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 RefundSignedExtension - for RefundBridgedMessages +/// Verify that the messages pallet call, supported by extension has succeeded. +pub(crate) fn verify_messages_call_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool where - Self: 'static + Send + Sync, - Runtime: MessagesConfig + RelayersConfig, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + MessagesCallSubType, + C: ExtensionConfig, + MI: 'static, + C::Runtime: BridgeMessagesConfig, { - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - vec![call] - } + let messages_call = call_info.messages_call_info(); - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, 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, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_call()?; - Ok(call) + if !MessagesCallHelper::::was_successful(messages_call) { + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages call", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false } - 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 - } + true } #[cfg(test)] -pub(crate) mod tests { +mod tests { use super::*; use crate::mock::*; - use bp_header_chain::StoredHeaderDataBuilder; + + use bp_header_chain::{StoredHeaderDataBuilder, SubmitFinalityProofInfo}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, DeliveredMessages, - InboundLaneData, LaneState, MessageNonce, MessagesOperatingMode, OutboundLaneData, - ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayer, - UnrewardedRelayerOccupation, UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, InboundLaneData, + LaneId, MessageNonce, MessagesCallInfo, MessagesOperatingMode, OutboundLaneData, + ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, + UnrewardedRelayersState, }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_parachains::{BestParaHeadHash, ParaInfo, SubmitParachainHeadsInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; + use bp_relayers::RuntimeWithUtilityPallet; + use bp_runtime::{BasicOperatingMode, HeaderId, Parachain}; use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; use frame_support::{ + __private::sp_tracing, assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, weights::Weight, }; use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; + use pallet_bridge_parachains::{Call as ParachainsCall, Pallet as ParachainsPallet}; + use pallet_utility::Call as UtilityCall; use sp_runtime::{ traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, @@ -964,7 +451,7 @@ pub(crate) mod tests { }; parameter_types! { - TestParachain: u32 = 1000; + TestParachain: u32 = BridgedUnderlyingParachain::PARACHAIN_ID; pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( test_lane_id(), TEST_BRIDGED_CHAIN_ID, @@ -980,35 +467,32 @@ pub(crate) mod tests { bp_runtime::generate_static_str_provider!(TestExtension); - type TestMessagesExtensionProvider = RefundBridgedMessages< - TestRuntime, - RefundableMessagesLane, - ActualFeeRefund, - ConstU64<1>, - StrTestExtension, - >; - type TestMessagesExtension = RefundSignedExtensionAdapter; - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< + type TestGrandpaExtensionConfig = grandpa_adapter::WithGrandpaChainExtensionConfig< + StrTestId, TestRuntime, + RuntimeWithUtilityPallet, + (), (), - RefundableMessagesLane, - ActualFeeRefund, ConstU64<1>, - StrTestExtension, >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< + type TestGrandpaExtension = + BridgeRelayersSignedExtension; + type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< + StrTestId, TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, - RefundableMessagesLane, - ActualFeeRefund, + RuntimeWithUtilityPallet, + (), + (), ConstU64<1>, - StrTestExtension, >; - type TestExtension = RefundSignedExtensionAdapter; + type TestExtension = BridgeRelayersSignedExtension; + + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); ExistentialDeposit::get().saturating_add(test_stake * 100) } @@ -1023,7 +507,7 @@ pub(crate) mod tests { TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) } - pub fn relayer_account_at_this_chain() -> ThisChainAccountId { + fn relayer_account_at_this_chain() -> ThisChainAccountId { 0 } @@ -1031,13 +515,13 @@ pub(crate) mod tests { 0 } - pub fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); pallet_bridge_grandpa::CurrentAuthoritySet::::put( StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), ); @@ -1047,7 +531,7 @@ pub(crate) mod tests { bp_test_utils::test_header::(0).build(), ); - let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_id = ParaId(TestParachain::get()); let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, @@ -1075,7 +559,7 @@ pub(crate) mod tests { .unwrap(); } - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1091,7 +575,7 @@ pub(crate) mod tests { }) } - pub fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call_ex(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1110,12 +594,12 @@ pub(crate) mod tests { } fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1123,12 +607,12 @@ pub(crate) mod tests { } pub fn submit_parachain_head_call_ex( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads_ex { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1171,7 +655,7 @@ pub(crate) mod tests { } fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1183,7 +667,7 @@ pub(crate) mod tests { } fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1195,7 +679,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1207,7 +691,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1219,7 +703,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1231,7 +715,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1243,8 +727,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1257,8 +741,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1271,8 +755,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1285,8 +769,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1298,10 +782,11 @@ pub(crate) mod tests { }) } - fn all_finality_pre_dispatch_data() -> PreDispatchData { + fn all_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1312,7 +797,7 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, @@ -1324,26 +809,28 @@ pub(crate) mod tests { }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { + #[cfg(test)] + fn all_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1354,7 +841,7 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, @@ -1369,17 +856,18 @@ pub(crate) mod tests { } } - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1396,26 +884,27 @@ pub(crate) mod tests { }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1435,20 +924,21 @@ pub(crate) mod tests { } } - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, @@ -1460,22 +950,23 @@ pub(crate) mod tests { }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, @@ -1490,10 +981,11 @@ pub(crate) mod tests { } } - fn delivery_pre_dispatch_data() -> PreDispatchData { + fn delivery_pre_dispatch_data() -> PreDispatchData + { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { lane_id: test_lane_id(), @@ -1502,19 +994,20 @@ pub(crate) mod tests { }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }, )), } } - fn confirmation_pre_dispatch_data() -> PreDispatchData { + fn confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: test_lane_id(), bundled_range: 101..=200, @@ -1525,14 +1018,14 @@ pub(crate) mod tests { } fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, + mut pre_dispatch_data: PreDispatchData, end: MessageNonce, - ) -> PreDispatchData { + ) -> PreDispatchData { let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, + ExtensionCallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, + ExtensionCallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::Msgs(ref mut info) => info, }; if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { @@ -1543,22 +1036,21 @@ pub(crate) mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } - fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } + // TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 + // 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| { @@ -1569,27 +1061,33 @@ pub(crate) mod tests { fn run_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + sp_tracing::try_init_simple(); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } - fn run_messages_pre_dispatch( - call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } + // TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 + // fn run_messages_pre_dispatch( + // call: RuntimeCall, + // ) -> Result>, TransactionValidityError> { + // let extension: TestMessagesExtension = + // RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + // extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + // } fn dispatch_info() -> DispatchInfo { DispatchInfo { @@ -1607,7 +1105,7 @@ pub(crate) mod tests { } fn run_post_dispatch( - pre_dispatch_data: Option>, + pre_dispatch_data: Option>, dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( @@ -1649,46 +1147,28 @@ pub(crate) mod tests { Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); // message delivery is failing - 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(message_delivery_call(200)), Ok(Default::default()),); assert_eq!( - ignore_priority(run_validate(message_confirmation_call(200))), + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Default::default()), ); + // message confirmation validation is passing assert_eq!( - ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( - 200, 200 - ))), + ignore_priority(run_validate(message_confirmation_call(200))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call( - 200, 200, 200 + ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( + 200, 200 ))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( + ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(Default::default()), @@ -1704,28 +1184,25 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - 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_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 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 - ); - } + 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 + ); }); } @@ -1737,26 +1214,23 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - 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 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, - )) - .unwrap() - .priority; + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 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, + ); }); } @@ -1773,15 +1247,11 @@ pub(crate) mod tests { ignore_priority(run_validate(message_confirmation_call(200))), Ok(ValidTransaction::default()), ); - - assert_eq!( - ignore_priority(run_messages_validate(message_delivery_call(200))), - Ok(ValidTransaction::default()), - ); - assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), - Ok(ValidTransaction::default()), - ); + // TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 + // assert_eq!( + // ignore_priority(run_messages_validate(message_delivery_call(200))), + // Ok(ValidTransaction::default()), + // ); assert_eq!( ignore_priority(run_validate(parachain_finality_and_delivery_batch_call(200, 200))), @@ -1798,24 +1268,12 @@ pub(crate) mod tests { ignore_priority(run_validate(all_finality_and_delivery_batch_call(200, 200, 200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); }); } @@ -2101,13 +1559,10 @@ pub(crate) mod tests { let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), + at_relay_block: (100, BridgedChainHash::default()), parachains: vec![ - (ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), [1u8; 32].into()), - ( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID + 1), - [1u8; 32].into(), - ), + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), ], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, }), @@ -2248,7 +1703,7 @@ pub(crate) mod tests { // now repeat the same with size+weight refund: we expect smaller reward let mut pre_dispatch_data = all_finality_pre_dispatch_data(); match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, ..) => { info.extra_weight.set_ref_time( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, ); @@ -2354,7 +1809,7 @@ pub(crate) mod tests { let delivery_rewards_account_balance = Balances::free_balance(delivery_rewards_account()); - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); Balances::set_balance( &relayer_account_at_this_chain(), ExistentialDeposit::get() + test_stake * 10, @@ -2424,10 +1879,10 @@ pub(crate) mod tests { } fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, + pre_dispatch_data: PreDispatchData, dispatch_result: DispatchResult, ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( + TestExtension::analyze_call_result( Some(Some(pre_dispatch_data)), &dispatch_info(), &post_dispatch_info(), @@ -2491,148 +1946,6 @@ pub(crate) 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(|| { @@ -2640,35 +1953,23 @@ pub(crate) mod tests { // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestGrandpaExtensionProvider::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!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestGrandpaExtensionProvider::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!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2676,7 +1977,7 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), @@ -2684,35 +1985,23 @@ pub(crate) mod tests { // relay + message delivery call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), Ok(Some(relay_finality_pre_dispatch_data().call_info)), ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), - ); // relay + message confirmation call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), - ); // message delivery call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2720,7 +2009,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2728,6 +2017,149 @@ pub(crate) mod tests { }); } + // TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102 + // #[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_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { @@ -2867,40 +2299,4 @@ pub(crate) mod tests { ); }); } - - #[test] - fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { - run_test(|| { - let best_delivered_message = - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - initialize_environment(100, 100, best_delivered_message); - - // register relayer so it gets priority boost - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - // allow empty message delivery transactions - let lane_id = test_lane_id(); - let in_lane_data = InboundLaneData { - state: LaneState::Opened, - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { - relayer: relayer_account_at_bridged_chain(), - messages: DeliveredMessages { begin: 1, end: best_delivered_message }, - }] - .into(), - }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); - - // now check that the priority of empty tx is the same as priority of 1-message tx - let priority_of_zero_messages_delivery = - run_validate(message_delivery_call(best_delivered_message)).unwrap().priority; - let priority_of_one_messages_delivery = - run_validate(message_delivery_call(best_delivered_message + 1)) - .unwrap() - .priority; - - assert_eq!(priority_of_zero_messages_delivery, priority_of_one_messages_delivery); - }); - } } diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs new file mode 100644 index 0000000000000..60b2f0f4e2d01 --- /dev/null +++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs @@ -0,0 +1,185 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote parachain. + +use crate::{ + extension::{ + grandpa_adapter::verify_submit_finality_proof_succeeded, verify_messages_call_succeeded, + }, + Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Parachain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use pallet_bridge_parachains::{ + CallSubType as BridgeParachainsCallSubtype, Config as BridgeParachainsConfig, + SubmitParachainHeadsHelper, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote parachains. +pub struct WithParachainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeParachainsConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-parachains`, tracked by this extension + BridgeParachainsPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeParachainsPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithParachainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig + + BridgeParachainsConfig + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + PI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeParachainsCallSubtype + + BridgeMessagesCallSubType, + >::BridgedChain: Parachain, +{ + type IdProvider = ID; + type Runtime = R; + type BatchCallUnpacker = BCU; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = + pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + log::trace!(target: LOG_TARGET, "=== call: {:?}", call); + let calls = Self::BatchCallUnpacker::unpack(call, 3); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let para_finality_call = calls.next().transpose()?.and_then(|c| { + let r = c.submit_parachain_heads_info_for( + >::BridgedChain::PARACHAIN_ID, + ); + r + }); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + log::trace!(target: LOG_TARGET, "=== {} {:?} {:?} {:?}", total_calls, relay_finality_call, para_finality_call, msgs_call); + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::AllFinalityAndMsgs( + relay_finality_call, + para_finality_call, + msgs_call, + )), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::( + call_info, call_data, relayer, + ) && verify_submit_parachain_head_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the parachain state update call, verify that it +/// has been successful. +/// +/// Only returns false when parachain state update call has failed. +pub(crate) fn verify_submit_parachain_head_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + PI: 'static, + C::Runtime: BridgeParachainsConfig, +{ + let Some(para_proof_info) = call_info.submit_parachain_heads_info() else { return true }; + + if !SubmitParachainHeadsHelper::::was_successful(para_proof_info) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid parachain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + true +} diff --git a/bridges/modules/relayers/src/extension/priority.rs b/bridges/modules/relayers/src/extension/priority.rs new file mode 100644 index 0000000000000..14511ad2545f1 --- /dev/null +++ b/bridges/modules/relayers/src/extension/priority.rs @@ -0,0 +1,202 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Bridge transaction priority calculator. +//! +//! We want to prioritize message delivery transactions with more messages over +//! transactions with less messages. That's because we reject delivery transactions +//! if it contains already delivered message. And if some transaction delivers +//! 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; + +// reexport everything from `integrity_tests` module +pub use integrity_tests::*; + +/// Compute priority boost for message delivery transaction that delivers +/// given number of messages. +pub fn compute_priority_boost( + messages: MessageNonce, +) -> TransactionPriority +where + PriorityBoostPerMessage: Get, +{ + // we don't want any boost for transaction with single message => minus one + PriorityBoostPerMessage::get().saturating_mul(messages.saturating_sub(1)) +} + +#[cfg(not(feature = "integrity-test"))] +mod integrity_tests {} + +#[cfg(feature = "integrity-test")] +mod integrity_tests { + use super::compute_priority_boost; + use bp_messages::ChainWithMessages; + + use bp_messages::MessageNonce; + use bp_runtime::PreComputedSize; + use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, Pays, PostDispatchInfo}, + traits::Get, + }; + use pallet_bridge_messages::WeightInfoExt; + use pallet_transaction_payment::OnChargeTransaction; + use sp_runtime::{ + traits::{Dispatchable, UniqueSaturatedInto, Zero}, + transaction_validity::TransactionPriority, + FixedPointOperand, SaturatedConversion, Saturating, + }; + + type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction< + T, + >>::Balance; + + /// 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( + tip_boost_per_message: BalanceOf, + ) where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + PriorityBoostPerMessage: Get, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + let priority_boost_per_message = PriorityBoostPerMessage::get(); + let maximal_messages_in_delivery_transaction = + Runtime::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + 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::(messages); + let priority_with_boost = base_priority + priority_boost; + + let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into()); + let priority_with_tip = + estimate_message_delivery_transaction_priority::(1, tip); + + const ERROR_MARGIN: TransactionPriority = 5; // 5% + if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) / + priority_with_tip > + ERROR_MARGIN + { + panic!( + "The PriorityBoostPerMessage value ({}) must be fixed to: {}", + priority_boost_per_message, + compute_priority_boost_per_message::( + tip_boost_per_message + ), + ); + } + } + } + + /// Compute priority boost that we give to message delivery transaction for additional message. + #[cfg(feature = "integrity-test")] + fn compute_priority_boost_per_message( + tip_boost_per_message: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + // esimate priority of transaction that delivers one message and has large tip + let maximal_messages_in_delivery_transaction = + Runtime::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + let small_with_tip_priority = + estimate_message_delivery_transaction_priority::( + 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()); + + small_with_tip_priority + .saturating_sub(large_without_tip_priority) + .saturating_div(maximal_messages_in_delivery_transaction - 1) + } + + /// Estimate message delivery transaction priority. + #[cfg(feature = "integrity-test")] + fn estimate_message_delivery_transaction_priority( + messages: MessageNonce, + tip: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: 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 extept 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::::get_priority( + &DispatchInfo { + weight: transaction_weight, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }, + transaction_size as _, + tip, + Zero::zero(), + ) + } +} diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 114f8ca3fb1ce..b9627774db1ec 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -36,13 +36,13 @@ pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; -pub mod benchmarking; - mod mock; mod payment_adapter; mod stake_adapter; mod weights_ext; +pub mod benchmarking; +pub mod extension; pub mod weights; /// The target that will be used when publishing logs related to this pallet. @@ -502,7 +502,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { + event: TestEvent::BridgeRelayers(RewardRegistered { relayer: REGULAR_RELAYER, rewards_account_params: test_reward_account_param(), reward: 100 @@ -581,7 +581,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { + event: TestEvent::BridgeRelayers(RewardPaid { relayer: REGULAR_RELAYER, rewards_account_params: test_reward_account_param(), reward: 100 @@ -595,7 +595,8 @@ mod tests { #[test] fn pay_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; + type PayLaneRewardFromAccount = + bp_relayers::PayRewardFromAccount; run_test(|| { let in_lane_0 = RewardsAccountParams::new( @@ -676,7 +677,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() }, }), @@ -744,7 +745,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -808,7 +809,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -870,7 +871,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), + event: TestEvent::BridgeRelayers(Event::Deregistered { + relayer: REGISTER_RELAYER + }), topics: vec![], }), ); diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 81993589de619..43aeec8bfc3b4 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -18,51 +18,187 @@ use crate as pallet_bridge_relayers; -use bp_messages::LaneId; +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + ChainWithMessages, LaneId, MessageNonce, +}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::{ PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, }; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, + derive_impl, parameter_types, + traits::fungible::Mutate, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_core::{ConstU64, ConstU8, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, ConstU32}, + BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; -use sp_runtime::BuildStorage; -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; +// generate identifier of the signed extension +bp_runtime::generate_static_str_provider!(TestId); + +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlockU32; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + const ID: ChainId = *b"tuch"; + + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithMessages for ThisUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingParachain; + +impl Chain for BridgedUnderlyingParachain { + const ID: ChainId = TEST_BRIDGED_CHAIN_ID; + + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithGrandpa for BridgedUnderlyingParachain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; +} + +impl ChainWithMessages for BridgedUnderlyingParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; + const MAX_HEADER_SIZE: u32 = 1_024; +} pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, + ThisChainAccountId, + ThisChainBlockNumber, Balances, ReserveId, Stake, Lease, >; -type Block = frame_system::mocking::MockBlock; - frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } } parameter_types! { + pub const BridgedParasPalletName: &'static str = "Paras"; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; + pub const ExistentialDeposit: ThisChainBalance = 1; pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; + pub const Stake: ThisChainBalance = 1_000; + pub const Lease: ThisChainBlockNumber = 8; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; + type Block = ThisChainBlock; + // TODO: remove when https://github.com/paritytech/polkadot-sdk/pull/4543 merged + type BlockHashCount = ConstU32<10>; + type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } @@ -72,9 +208,74 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = BridgedUnderlyingParachain; + type MaxFreeHeadersPerBlock = ConstU32<4>; + type FreeHeadersInterval = ConstU32<1_024>; + type HeadersToKeep = ConstU32<8>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + + type OutboundPayload = Vec; + + type InboundPayload = Vec; + type DeliveryPayments = (); + + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + (), + ConstU64<100_000>, + >; + type OnMessagesDelivered = (); + + type MessageDispatch = DummyMessageDispatch; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingParachain; + type BridgedHeaderChain = BridgeGrandpa; +} + impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = Balance; + type Reward = ThisChainBalance; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); @@ -82,9 +283,9 @@ impl pallet_bridge_relayers::Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { + fn prepare_rewards_account(account_params: RewardsAccountParams, reward: ThisChainBalance) { let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( + bp_relayers::PayRewardFromAccount::::rewards_account( account_params, ); Self::deposit_account(rewards_account, reward); @@ -96,30 +297,30 @@ impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { } /// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; +pub const REGULAR_RELAYER: ThisChainAccountId = 1; /// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; +pub const FAILING_RELAYER: ThisChainAccountId = 2; /// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; +pub const REGISTER_RELAYER: ThisChainAccountId = 42; /// Payment procedure that rejects payments to the `FAILING_RELAYER`. pub struct TestPaymentProcedure; impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) + pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId { + PayRewardFromAccount::<(), ThisChainAccountId>::rewards_account(params) } } -impl PaymentProcedure for TestPaymentProcedure { +impl PaymentProcedure for TestPaymentProcedure { type Error = (); fn pay_reward( - relayer: &AccountId, + relayer: &ThisChainAccountId, _lane_id: RewardsAccountParams, - _reward: Balance, + _reward: ThisChainBalance, ) -> Result<(), Self::Error> { match *relayer { FAILING_RELAYER => Err(()), @@ -128,6 +329,35 @@ impl PaymentProcedure for TestPaymentProcedure { } } +/// Dummy message dispatcher. +pub struct DummyMessageDispatch; + +impl DummyMessageDispatch { + pub fn deactivate(lane: LaneId) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); + } +} + +impl MessageDispatch for DummyMessageDispatch { + type DispatchPayload = Vec; + type DispatchLevelResult = (); + + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) + } + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + Weight::zero() + } + + fn dispatch( + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} + /// Reward account params that we are using in tests. pub fn test_reward_account_param() -> RewardsAccountParams { RewardsAccountParams::new(LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain) diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs index beec412c12035..3693793a3e5ca 100644 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ b/bridges/modules/relayers/src/payment_adapter.rs @@ -103,11 +103,11 @@ mod tests { use super::*; use crate::{mock::*, RelayerRewards}; - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; + const RELAYER_1: ThisChainAccountId = 1; + const RELAYER_2: ThisChainAccountId = 2; + const RELAYER_3: ThisChainAccountId = 3; - fn relayers_rewards() -> RelayersRewards { + fn relayers_rewards() -> RelayersRewards { vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() } diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs index ce097344d56be..0c965e9e6bff8 100644 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ b/bridges/modules/relayers/src/stake_adapter.rs @@ -80,7 +80,7 @@ mod tests { use frame_support::traits::fungible::Mutate; - fn test_stake() -> Balance { + fn test_stake() -> ThisChainBalance { Stake::get() } diff --git a/bridges/primitives/header-chain/src/call_info.rs b/bridges/primitives/header-chain/src/call_info.rs index 6f880e9394147..acf7447adabc7 100644 --- a/bridges/primitives/header-chain/src/call_info.rs +++ b/bridges/primitives/header-chain/src/call_info.rs @@ -20,14 +20,11 @@ use crate::{justification, InitializationData}; use bp_runtime::HeaderOf; use codec::{Decode, Encode}; -use frame_support::weights::Weight; +use frame_support::{weights::Weight, RuntimeDebugNoBound}; use scale_info::TypeInfo; use sp_consensus_grandpa::SetId; -use sp_runtime::{ - traits::{Header as HeaderT, Zero}, - RuntimeDebug, -}; -use sp_std::boxed::Box; +use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_std::{boxed::Box, fmt::Debug}; /// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -63,8 +60,8 @@ pub enum BridgeGrandpaCall { pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; /// A digest information on the `BridgeGrandpaCall::submit_finality_proof` call. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct SubmitFinalityProofInfo { +#[derive(Copy, Clone, PartialEq, RuntimeDebugNoBound)] +pub struct SubmitFinalityProofInfo { /// Number of the finality target. pub block_number: N, /// An identifier of the validators set that has signed the submitted justification. @@ -89,7 +86,7 @@ pub struct SubmitFinalityProofInfo { pub extra_size: u32, } -impl SubmitFinalityProofInfo { +impl SubmitFinalityProofInfo { /// Returns `true` if call size/weight is below our estimations for regular calls. pub fn fits_limits(&self) -> bool { self.extra_weight.is_zero() && self.extra_size.is_zero() diff --git a/bridges/primitives/messages/src/call_info.rs b/bridges/primitives/messages/src/call_info.rs index 4cc3b9d4afea7..c8f06ed8cb7c3 100644 --- a/bridges/primitives/messages/src/call_info.rs +++ b/bridges/primitives/messages/src/call_info.rs @@ -154,6 +154,14 @@ pub enum MessagesCallInfo { } impl MessagesCallInfo { + /// Returns lane, used by the call. + pub fn lane_id(&self) -> LaneId { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.lane_id, + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.lane_id, + } + } + /// Returns range of messages, bundled with the call. pub fn bundled_messages(&self) -> RangeInclusive { match *self { diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs index 9717fee8597c7..ec3bf9ca3a0af 100644 --- a/bridges/primitives/parachains/src/lib.rs +++ b/bridges/primitives/parachains/src/lib.rs @@ -20,11 +20,9 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use bp_header_chain::StoredHeaderData; +pub use call_info::{BridgeParachainCall, SubmitParachainHeadsInfo}; -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaId}, - BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, -}; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaId}; use bp_runtime::{ BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, StorageMapKeyProvider, @@ -36,7 +34,12 @@ use sp_core::storage::StorageKey; use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; use sp_std::{marker::PhantomData, prelude::*}; -pub use call_info::{BridgeParachainCall, SubmitParachainHeadsInfo}; +/// Block hash of the bridged relay chain. +pub type RelayBlockHash = bp_polkadot_core::Hash; +/// Block number of the bridged relay chain. +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +/// Hasher of the bridged relay chain. +pub type RelayBlockHasher = bp_polkadot_core::Hasher; mod call_info; diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 5081dddce1e61..fb7e88dbafffb 100644 --- a/bridges/primitives/relayers/Cargo.toml +++ b/bridges/primitives/relayers/Cargo.toml @@ -16,12 +16,16 @@ scale-info = { version = "2.11.1", default-features = false, features = ["bit-ve # Bridge Dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-parachains = { path = "../parachains", default-features = false } +bp-runtime = { path = "../runtime", default-features = false, features = ["test-helpers"] } # Substrate Dependencies +frame-system = { path = "../../../substrate/frame/system", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } +pallet-utility = { path = "../../../substrate/frame/utility", default-features = false } sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } sp-std = { path = "../../../substrate/primitives/std", default-features = false } @@ -32,10 +36,14 @@ hex-literal = "0.4" [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-runtime/std", "codec/std", "frame-support/std", + "frame-system/std", + "pallet-utility/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/bridges/primitives/relayers/src/extension.rs b/bridges/primitives/relayers/src/extension.rs new file mode 100644 index 0000000000000..91d0a92b0de31 --- /dev/null +++ b/bridges/primitives/relayers/src/extension.rs @@ -0,0 +1,193 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! All runtime calls, supported by `pallet-bridge-relayers` when it acts as a signed +//! extension. + +use bp_header_chain::SubmitFinalityProofInfo; +use bp_messages::MessagesCallInfo; +use bp_parachains::SubmitParachainHeadsInfo; +use bp_runtime::StaticStrProvider; +use frame_support::{ + dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound, +}; +use frame_system::Config as SystemConfig; +use pallet_utility::{Call as UtilityCall, Pallet as UtilityPallet}; +use sp_runtime::{ + traits::Get, + transaction_validity::{TransactionPriority, TransactionValidityError}, + RuntimeDebug, +}; +use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; + +/// Type of the call that the signed extension recognizes. +#[derive(PartialEq, RuntimeDebugNoBound)] +pub enum ExtensionCallInfo { + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + MessagesCallInfo, + ), + /// Relay chain finality + message delivery/confirmation calls. + RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), + /// Parachain finality + message delivery/confirmation calls. + /// + /// This variant is used only when bridging with parachain. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), +} + +impl + ExtensionCallInfo +{ + /// Returns true if call is a message delivery call (with optional finality calls). + pub fn is_receive_messages_proof_call(&self) -> bool { + match self.messages_call_info() { + MessagesCallInfo::ReceiveMessagesProof(_) => true, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, + } + } + + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + pub fn submit_finality_proof_info( + &self, + ) -> Option> { + match *self { + Self::AllFinalityAndMsgs(info, _, _) => Some(info), + Self::RelayFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + pub fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + pub fn messages_call_info(&self) -> &MessagesCallInfo { + match self { + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::RelayFinalityAndMsgs(_, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, + } + } +} + +/// Extra post-dispatch data, associated with the supported runtime call. +#[derive(Default, RuntimeDebug)] +pub struct ExtensionCallData { + /// Extra weight, consumed by the call. We have some assumptions about normal weight + /// that may be consumed by expected calls. If the actual weight is larger than that, + /// we do not refund relayer for this extra weight. + pub extra_weight: Weight, + /// Extra size, consumed by the call. We have some assumptions about normal size + /// of the encoded call. If the actual size is larger than that, we do not refund relayer + /// for this extra size. + pub extra_size: u32, +} + +/// Signed extension configuration. +/// +/// The single `pallet-bridge-relayers` instance may be shared by multiple messages +/// pallet instances, bridging with different remote networks. We expect every instance +/// of the messages pallet to add a separate signed extension to runtime. So it must +/// have a separate configuration. +pub trait ExtensionConfig { + /// Unique identifier of the signed extension that will use this configuration. + type IdProvider: StaticStrProvider; + /// Runtime that optionally supports batched calls. We assume that batched call + /// succeeds if and only if all of its nested calls succeed. + type Runtime: frame_system::Config; + /// Batch calls unpacker. + type BatchCallUnpacker: BatchCallUnpacker; + /// Messages pallet instance. + type BridgeMessagesPalletInstance: 'static; + /// Additional priority that is added to base message delivery transaction priority + /// for every additional bundled message. + type PriorityBoostPerMessage: Get; + /// Type of reward, that the `pallet-bridge-relayers` is using. + type Reward; + /// Block number for the remote **GRANDPA chain**. Mind that this chain is not + /// necessarily the chain that we are bridging with. If we are bridging with + /// parachain, it must be its parent relay chain. If we are bridging with the + /// GRANDPA chain, it must be it. + type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug; + + /// Given runtime call, check if it is supported by the signed extension. Additionally, + /// check if call (or any of batched calls) are obsolete. + fn parse_and_check_for_obsolete_call( + call: &::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + >; + + /// Check if runtime call is already obsolete. + fn check_obsolete_parsed_call( + call: &::RuntimeCall, + ) -> Result<&::RuntimeCall, TransactionValidityError>; + + /// Given runtime call info, check that this call has been successful and has updated + /// runtime storage accordingly. + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, + ) -> bool; +} + +/// Something that can unpack batch calls (all-or-nothing flavor) of given size. +pub trait BatchCallUnpacker { + /// Unpack batch call with no more than `max_packed_calls` calls. + fn unpack(call: &Runtime::RuntimeCall, max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall>; +} + +/// An `BatchCallUnpacker` adapter for runtimes with utility pallet. +pub struct RuntimeWithUtilityPallet(PhantomData); + +impl BatchCallUnpacker for RuntimeWithUtilityPallet +where + Runtime: pallet_utility::Config::RuntimeCall>, + ::RuntimeCall: + IsSubType, Runtime>>, +{ + fn unpack( + call: &::RuntimeCall, + max_packed_calls: u32, + ) -> Vec<&::RuntimeCall> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) + if calls.len() <= max_packed_calls as usize => + calls.iter().collect(), + Some(_) => vec![], + None => vec![call], + } + } +} + +impl BatchCallUnpacker for () { + fn unpack(call: &Runtime::RuntimeCall, _max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall> { + vec![call] + } +} diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index 704fef7c8e6ba..1e63c89ecd704 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -19,6 +19,10 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +pub use extension::{ + BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RuntimeWithUtilityPallet, +}; pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash}; use bp_messages::LaneId; @@ -32,6 +36,7 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker::PhantomData}; +mod extension; mod registration; /// The owner of the sovereign account that should pay the rewards. diff --git a/bridges/relays/lib-substrate-relay/Cargo.toml b/bridges/relays/lib-substrate-relay/Cargo.toml index 08633a2bc6873..fc9acf260d28a 100644 --- a/bridges/relays/lib-substrate-relay/Cargo.toml +++ b/bridges/relays/lib-substrate-relay/Cargo.toml @@ -31,7 +31,6 @@ bp-header-chain = { path = "../../primitives/header-chain" } bp-parachains = { path = "../../primitives/parachains" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-relayers = { path = "../../primitives/relayers" } -bridge-runtime-common = { path = "../../bin/runtime-common" } equivocation-detector = { path = "../equivocation" } finality-grandpa = { version = "0.16.2" } diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs index 5631285b3c544..28b0eb0ad5267 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs @@ -22,7 +22,7 @@ use crate::{ messages::{MessagesRelayLimits, SubstrateMessageLane}, parachains::SubstrateParachainsPipeline, }; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain, }; diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs index 8104be7af807a..e8b797f84fa51 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -30,8 +30,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs index 6c078973fedc0..f9884ee197b40 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -33,8 +33,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs index 4579222a2c681..2ef86f48ecbeb 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -31,11 +31,11 @@ use async_std::{ sync::{Arc, Mutex}, }; use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::{ is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index 8b128bb770dd7..08d8e5e2a4f55 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs @@ -18,11 +18,9 @@ //! parachain finality proofs synchronization pipelines. use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockHasher, RelayBlockNumber, -}; +use pallet_bridge_parachains::{Call as BridgeParachainsCall, Config as BridgeParachainsConfig}; use parachains_relay::ParachainsPipeline; use relay_substrate_client::{ CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain,