diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json index 747b2bb4ac8fb..52619e1cfe574 100644 --- a/.github/workflows/runtimes-matrix.json +++ b/.github/workflows/runtimes-matrix.json @@ -67,7 +67,7 @@ }, { "name": "bridge-hub-westend", - "package": "bridge-hub-rococo-runtime", + "package": "bridge-hub-westend-runtime", "path": "cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend", "header": "cumulus/file_header.txt", "template": "cumulus/templates/xcm-bench-template.hbs", diff --git a/Cargo.lock b/Cargo.lock index 493494c423b27..6238c0293c7e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12745,7 +12745,6 @@ dependencies = [ "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0", ] [[package]] diff --git a/bridges/bin/runtime-common/src/extensions.rs b/bridges/bin/runtime-common/src/extensions.rs index 44e6b40b7e0c7..a8ae17d23128e 100644 --- a/bridges/bin/runtime-common/src/extensions.rs +++ b/bridges/bin/runtime-common/src/extensions.rs @@ -111,7 +111,7 @@ where // let's slash registered relayer RelayersPallet::::slash_and_deregister( relayer, - ExplicitOrAccountParams::Explicit(SlashAccount::get()), + ExplicitOrAccountParams::Explicit::<_, ()>(SlashAccount::get()), ); } } @@ -182,7 +182,7 @@ where // let's slash registered relayer RelayersPallet::::slash_and_deregister( relayer, - ExplicitOrAccountParams::Explicit(SlashAccount::get()), + ExplicitOrAccountParams::Explicit::<_, ()>(SlashAccount::get()), ); } } diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 88037d9deff52..3e39fb8423dc3 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -24,7 +24,7 @@ use bp_messages::{ ChainWithMessages, HashedLaneId, LaneIdType, MessageNonce, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_relayers::PayRewardFromAccount; +use bp_relayers::{PayRewardFromAccount, RewardsAccountParams}; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; use codec::Encode; use frame_support::{ @@ -70,7 +70,8 @@ pub type BridgedChainHeader = sp_runtime::generic::Header; /// Rewards payment procedure. -pub type TestPaymentProcedure = PayRewardFromAccount; +pub type TestPaymentProcedure = + PayRewardFromAccount; /// Stake that we are using in tests. pub type TestStake = ConstU64<5_000>; /// Stake and slash mechanism to use in tests. @@ -89,6 +90,8 @@ pub type TestLaneIdType = HashedLaneId; pub fn test_lane_id() -> TestLaneIdType { TestLaneIdType::try_new(1, 2).unwrap() } +/// Reward measurement type. +pub type Reward = u32; /// Bridged chain id used in tests. pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; @@ -197,7 +200,7 @@ impl pallet_bridge_messages::Config for TestRuntime { TestRuntime, (), (), - ConstU64<100_000>, + ConstU32<100_000>, >; type OnMessagesDelivered = (); @@ -210,11 +213,12 @@ impl pallet_bridge_messages::Config for TestRuntime { impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = ThisChainBalance; + type Reward = Reward; + type RewardKind = RewardsAccountParams>; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; + type Balance = ThisChainBalance; type WeightInfo = (); - type LaneId = TestLaneIdType; } /// Dummy message dispatcher. diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 97ed61a9004e8..d0b19f612d2a6 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -31,7 +31,6 @@ frame-system = { workspace = true } pallet-transaction-payment = { workspace = true } sp-arithmetic = { workspace = true } sp-runtime = { workspace = true } -sp-std = { workspace = true } [dev-dependencies] bp-parachains = { workspace = true } @@ -69,7 +68,6 @@ std = [ "sp-core/std", "sp-io/std", "sp-runtime/std", - "sp-std/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/bridges/modules/relayers/src/benchmarking.rs b/bridges/modules/relayers/src/benchmarking.rs index 8fe3fc11d6ae6..e72f6dbd77265 100644 --- a/bridges/modules/relayers/src/benchmarking.rs +++ b/bridges/modules/relayers/src/benchmarking.rs @@ -20,8 +20,10 @@ use crate::*; -use bp_relayers::RewardsAccountOwner; -use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::{ + benchmarks_instance_pallet, whitelisted_caller, BenchmarkError, BenchmarkResult, +}; +use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use sp_runtime::traits::One; @@ -33,35 +35,66 @@ pub struct Pallet, I: 'static = ()>(crate::Pallet); /// Trait that must be implemented by runtime. pub trait Config: crate::Config { - /// Lane id to use in benchmarks. - fn bench_lane_id() -> Self::LaneId { - Self::LaneId::default() - } + /// `T::RewardKind` to use in benchmarks. + fn bench_reward_kind() -> Self::RewardKind; /// Prepare environment for paying given reward for serving given lane. fn prepare_rewards_account( - account_params: RewardsAccountParams, + reward_kind: Self::RewardKind, reward: Self::Reward, - ); + ) -> Option>; /// Give enough balance to given account. - fn deposit_account(account: Self::AccountId, balance: Self::Reward); + fn deposit_account(account: Self::AccountId, balance: Self::Balance); +} + +fn assert_last_event, I: 'static>( + generic_event: >::RuntimeEvent, +) { + frame_system::Pallet::::assert_last_event(generic_event.into()); } benchmarks_instance_pallet! { // Benchmark `claim_rewards` call. claim_rewards { - let lane = T::bench_lane_id(); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); + let reward_kind = T::bench_reward_kind(); + let relayer: T::AccountId = whitelisted_caller(); + let reward = T::Reward::from(REWARD_AMOUNT); + + let _ = T::prepare_rewards_account(reward_kind, reward); + RelayerRewards::::insert(&relayer, reward_kind, reward); + }: _(RawOrigin::Signed(relayer.clone()), reward_kind) + verify { + // we can't check anything here, because `PaymentProcedure` is responsible for + // payment logic, so we assume that if call has succeeded, the procedure has + // also completed successfully + assert_last_event::(Event::RewardPaid { + relayer, + reward_kind, + reward, + alternative_beneficiary: None, + }.into()); + } + + // Benchmark `claim_rewards_to` call. + claim_rewards_to { + let reward_kind = T::bench_reward_kind(); let relayer: T::AccountId = whitelisted_caller(); let reward = T::Reward::from(REWARD_AMOUNT); - T::prepare_rewards_account(account_params, reward); - RelayerRewards::::insert(&relayer, account_params, reward); - }: _(RawOrigin::Signed(relayer), account_params) + let Some(alternative_beneficiary) = T::prepare_rewards_account(reward_kind, reward) else { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))); + }; + RelayerRewards::::insert(&relayer, reward_kind, reward); + }: _(RawOrigin::Signed(relayer.clone()), reward_kind, alternative_beneficiary.clone()) verify { // we can't check anything here, because `PaymentProcedure` is responsible for // payment logic, so we assume that if call has succeeded, the procedure has // also completed successfully + assert_last_event::(Event::RewardPaid { + relayer, + reward_kind, + reward, + alternative_beneficiary: Some(alternative_beneficiary), + }.into()); } // Benchmark `register` call. @@ -95,7 +128,7 @@ benchmarks_instance_pallet! { } // Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension + // the weight of message delivery call if `BridgeRelayersTransactionExtension` signed extension // is deployed at runtime level. slash_and_deregister { // prepare and register relayer account @@ -105,32 +138,30 @@ benchmarks_instance_pallet! { .saturating_add(One::one()) .saturating_add(One::one()); T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); + assert_ok!(crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till)); // create slash destination account - let lane = T::bench_lane_id(); - let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - T::prepare_rewards_account(slash_destination, Zero::zero()); + let slash_destination: T::AccountId = whitelisted_caller(); + T::deposit_account(slash_destination.clone(), Zero::zero()); }: { - crate::Pallet::::slash_and_deregister(&relayer, slash_destination.into()) + crate::Pallet::::slash_and_deregister(&relayer, bp_relayers::ExplicitOrAccountParams::Explicit::<_, ()>(slash_destination)) } verify { assert!(!crate::Pallet::::is_registration_active(&relayer)); } // Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension + // the weight of message delivery call if `BridgeRelayersTransactionExtension` signed extension // is deployed at runtime level. register_relayer_reward { - let lane = T::bench_lane_id(); + let reward_kind = T::bench_reward_kind(); let relayer: T::AccountId = whitelisted_caller(); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); + }: { - crate::Pallet::::register_relayer_reward(account_params, &relayer, One::one()); + crate::Pallet::::register_relayer_reward(reward_kind, &relayer, One::one()); } verify { - assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); + assert_eq!(RelayerRewards::::get(relayer, &reward_kind), Some(One::one())); } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs index 2a8a6e78ef9c7..9e98c73f8da04 100644 --- a/bridges/modules/relayers/src/extension/grandpa_adapter.rs +++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs @@ -23,6 +23,7 @@ use crate::{ use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; use bp_runtime::{Chain, StaticStrProvider}; +use core::marker::PhantomData; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_system::Config as SystemConfig; use pallet_bridge_grandpa::{ @@ -37,7 +38,6 @@ use sp_runtime::{ 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. diff --git a/bridges/modules/relayers/src/extension/messages_adapter.rs b/bridges/modules/relayers/src/extension/messages_adapter.rs index e8c2088b7f2d3..ffa8c5d8ab6d3 100644 --- a/bridges/modules/relayers/src/extension/messages_adapter.rs +++ b/bridges/modules/relayers/src/extension/messages_adapter.rs @@ -21,6 +21,7 @@ use crate::{extension::verify_messages_call_succeeded, Config as BridgeRelayersC use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; use bp_runtime::StaticStrProvider; +use core::marker::PhantomData; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use pallet_bridge_messages::{ CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf, @@ -29,7 +30,6 @@ use sp_runtime::{ traits::{Dispatchable, Get}, transaction_validity::{TransactionPriority, TransactionValidityError}, }; -use sp_std::marker::PhantomData; /// Transaction extension that refunds a relayer for standalone messages delivery and confirmation /// transactions. Finality transactions are not refunded. diff --git a/bridges/modules/relayers/src/extension/mod.rs b/bridges/modules/relayers/src/extension/mod.rs index d562ed9bcd0e8..d42e3f9a20fd5 100644 --- a/bridges/modules/relayers/src/extension/mod.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -31,6 +31,7 @@ use bp_relayers::{ }; use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; use codec::{Decode, Encode}; +use core::{fmt::Debug, marker::PhantomData}; use frame_support::{ dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::TransactionSource, @@ -53,7 +54,6 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransactionBuilder}, DispatchResult, RuntimeDebug, }; -use sp_std::{fmt::Debug, marker::PhantomData}; pub use grandpa_adapter::WithGrandpaChainExtensionConfig; pub use messages_adapter::WithMessagesExtensionConfig; @@ -111,6 +111,8 @@ pub enum RelayerAccountAction { /// It may be incorporated into runtime to refund relayers for submitting correct /// message delivery and confirmation transactions, optionally batched with required /// finality proofs. +/// +/// (Works only with `pallet-bridge-messages` and `RewardsAccountParams` as the `RewardKind`) #[derive( DefaultNoBound, CloneNoBound, @@ -122,22 +124,24 @@ pub enum RelayerAccountAction { TypeInfo, )] #[scale_info(skip_type_params(Runtime, Config, LaneId))] -pub struct BridgeRelayersTransactionExtension( - PhantomData<(Runtime, Config, LaneId)>, -); +pub struct BridgeRelayersTransactionExtension(PhantomData<(Runtime, Config)>); -impl BridgeRelayersTransactionExtension +impl BridgeRelayersTransactionExtension where Self: 'static + Send + Sync, - R: RelayersConfig - + BridgeMessagesConfig + R: RelayersConfig + + BridgeMessagesConfig + TransactionPaymentConfig, - C: ExtensionConfig, + C: ExtensionConfig, R::RuntimeCall: Dispatchable, ::RuntimeOrigin: AsSystemOriginSigner + Clone, - ::OnChargeTransaction: - OnChargeTransaction, - LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, + ::OnChargeTransaction: OnChargeTransaction, + >::RewardKind: + From>, + >::Reward: From< + <::OnChargeTransaction as OnChargeTransaction>::Balance, + >, + C::LaneId: From>, { /// Returns number of bundled messages `Some(_)`, if the given call info is a: /// @@ -149,7 +153,7 @@ where /// virtually boosted. The relayer registration (we only boost priority for registered /// relayer transactions) must be checked outside. fn bundled_messages_for_priority_boost( - parsed_call: &ExtensionCallInfo, + parsed_call: &ExtensionCallInfo, ) -> Option { // we only boost priority of message delivery transactions if !parsed_call.is_receive_messages_proof_call() { @@ -172,12 +176,12 @@ where /// 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 { + ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { Some(pre) => (pre.relayer, pre.call_info), @@ -260,7 +264,7 @@ where let refund = Self::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer - RelayerAccountAction::Reward(relayer, reward_account_params, refund) + RelayerAccountAction::Reward(relayer, reward_account_params, refund.into()) } /// Compute refund for the successful relayer transaction @@ -268,29 +272,33 @@ where info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, - tip: R::Reward, - ) -> R::Reward { + tip: <::OnChargeTransaction as OnChargeTransaction>::Balance, + ) -> <::OnChargeTransaction as OnChargeTransaction>::Balance + { TransactionPaymentPallet::::compute_actual_fee(len as _, info, post_info, tip) } } -impl TransactionExtension - for BridgeRelayersTransactionExtension +impl TransactionExtension for BridgeRelayersTransactionExtension where Self: 'static + Send + Sync, - R: RelayersConfig - + BridgeMessagesConfig + R: RelayersConfig + + BridgeMessagesConfig + TransactionPaymentConfig, - C: ExtensionConfig, + C: ExtensionConfig, R::RuntimeCall: Dispatchable, ::RuntimeOrigin: AsSystemOriginSigner + Clone, - ::OnChargeTransaction: - OnChargeTransaction, - LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, + ::OnChargeTransaction: OnChargeTransaction, + >::RewardKind: + From>, + >::Reward: From< + <::OnChargeTransaction as OnChargeTransaction>::Balance, + >, + C::LaneId: From>, { const IDENTIFIER: &'static str = C::IdProvider::STR; type Implicit = (); - type Pre = Option>; + type Pre = Option>; type Val = Self::Pre; fn weight(&self, _call: &R::RuntimeCall) -> Weight { @@ -385,7 +393,7 @@ where RelayerAccountAction::None => (), RelayerAccountAction::Reward(relayer, reward_account, reward) => { RelayersPallet::::register_relayer_reward( - reward_account, + reward_account.into(), &relayer, reward, ); @@ -505,7 +513,7 @@ mod tests { ConstU64<1>, >; type TestGrandpaExtension = - BridgeRelayersTransactionExtension; + BridgeRelayersTransactionExtension; type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< StrTestExtension, TestRuntime, @@ -515,8 +523,7 @@ mod tests { (), ConstU64<1>, >; - type TestExtension = - BridgeRelayersTransactionExtension; + type TestExtension = BridgeRelayersTransactionExtension; type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig< StrTestMessagesExtension, TestRuntime, @@ -524,11 +531,8 @@ mod tests { (), ConstU64<1>, >; - type TestMessagesExtension = BridgeRelayersTransactionExtension< - TestRuntime, - TestMessagesExtensionConfig, - TestLaneIdType, - >; + type TestMessagesExtension = + BridgeRelayersTransactionExtension; fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { let test_stake: ThisChainBalance = Stake::get(); @@ -1214,7 +1218,7 @@ mod tests { assert_eq!(post_dispatch_result, Ok(Weight::zero())); } - fn expected_delivery_reward() -> ThisChainBalance { + fn expected_delivery_reward() -> Reward { let mut post_dispatch_info = post_dispatch_info(); let extra_weight = ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(); post_dispatch_info.actual_weight = @@ -1227,7 +1231,7 @@ mod tests { ) } - fn expected_confirmation_reward() -> ThisChainBalance { + fn expected_confirmation_reward() -> Reward { pallet_transaction_payment::Pallet::::compute_actual_fee( 1024, &dispatch_info(), @@ -1980,7 +1984,7 @@ mod tests { TestLaneIdType, >, dispatch_result: DispatchResult, - ) -> RelayerAccountAction { + ) -> RelayerAccountAction { TestExtension::analyze_call_result( Some(pre_dispatch_data), &dispatch_info(), diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs index 69cf766dd674d..122e7073632fa 100644 --- a/bridges/modules/relayers/src/extension/parachain_adapter.rs +++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs @@ -26,6 +26,7 @@ use crate::{ use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; use bp_runtime::{Parachain, StaticStrProvider}; +use core::marker::PhantomData; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_system::Config as SystemConfig; use pallet_bridge_grandpa::{ @@ -42,7 +43,6 @@ 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< diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index d1c71b6d30510..cbf21668cff91 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -20,16 +20,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] -use bp_relayers::{ - ExplicitOrAccountParams, PaymentProcedure, Registration, RelayerRewardsKeyProvider, - StakeAndSlash, -}; -pub use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; +extern crate alloc; + +pub use bp_relayers::RewardLedger; +use bp_relayers::{PaymentProcedure, Registration, RelayerRewardsKeyProvider, StakeAndSlash}; use bp_runtime::StorageDoubleMapKeyProvider; -use frame_support::fail; +use core::marker::PhantomData; +use frame_support::{fail, traits::tokens::Balance}; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; -use sp_runtime::{traits::CheckedSub, Saturating}; -use sp_std::marker::PhantomData; +use sp_runtime::{ + traits::{CheckedSub, IdentifyAccount}, + Saturating, +}; pub use pallet::*; pub use payment_adapter::{DeliveryConfirmationPaymentsAdapter, PayRewardFromAccount}; @@ -53,36 +55,50 @@ pub const LOG_TARGET: &str = "runtime::bridge-relayers"; #[frame_support::pallet] pub mod pallet { use super::*; - use bp_messages::LaneIdType; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; /// `RelayerRewardsKeyProvider` for given configuration. type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< ::AccountId, + >::RewardKind, >::Reward, - >::LaneId, >; + /// Shortcut to alternative beneficiary type for `Config::PaymentProcedure`. + pub type AlternativeBeneficiaryOf = + <>::PaymentProcedure as PaymentProcedure< + ::AccountId, + >::RewardKind, + >::Reward, + >>::AlternativeBeneficiary; + #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type of relayer reward. type Reward: AtLeast32BitUnsigned + Copy + Member + Parameter + MaxEncodedLen; + /// Reward discriminator type. The pallet can collect different types of rewards for a + /// single account, so `RewardKind` is used as the second key in the `RelayerRewards` double + /// map. + /// + /// For example, rewards for different bridges can be stored, where `RewardKind` is + /// implemented as an enum representing each bridge. + type RewardKind: Parameter + MaxEncodedLen + Send + Sync + Copy + Clone; + /// Pay rewards scheme. - type PaymentProcedure: PaymentProcedure< - Self::AccountId, - Self::Reward, - LaneId = Self::LaneId, - >; + type PaymentProcedure: PaymentProcedure; + /// Stake and slash scheme. - type StakeAndSlash: StakeAndSlash, Self::Reward>; + type StakeAndSlash: StakeAndSlash, Self::Balance>; + /// Type for representing balance of an account used for `T::StakeAndSlash`. + type Balance: Balance; + /// Pallet call weights. type WeightInfo: WeightInfoExt; - /// Lane identifier type. - type LaneId: LaneIdType + Send + Sync; } #[pallet::pallet] @@ -94,37 +110,10 @@ pub mod pallet { /// Claim accumulated rewards. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::claim_rewards())] - pub fn claim_rewards( - origin: OriginFor, - rewards_account_params: RewardsAccountParams, - ) -> DispatchResult { + pub fn claim_rewards(origin: OriginFor, reward_kind: T::RewardKind) -> DispatchResult { let relayer = ensure_signed(origin)?; - RelayerRewards::::try_mutate_exists( - &relayer, - rewards_account_params, - |maybe_reward| -> DispatchResult { - let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; - T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) - .map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to pay {:?} rewards to {:?}: {:?}", - rewards_account_params, - relayer, - e, - ); - Error::::FailedToPayReward - })?; - - Self::deposit_event(Event::::RewardPaid { - relayer: relayer.clone(), - rewards_account_params, - reward, - }); - Ok(()) - }, - ) + Self::do_claim_rewards(relayer, reward_kind, None) } /// Register relayer or update its registration. @@ -229,9 +218,62 @@ pub mod pallet { }, ) } + + /// Claim accumulated rewards and send them to the alternative beneficiary. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::claim_rewards_to())] + pub fn claim_rewards_to( + origin: OriginFor, + reward_kind: T::RewardKind, + alternative_beneficiary: AlternativeBeneficiaryOf, + ) -> DispatchResult { + let relayer = ensure_signed(origin)?; + + Self::do_claim_rewards(relayer, reward_kind, Some(alternative_beneficiary)) + } } impl, I: 'static> Pallet { + fn do_claim_rewards( + relayer: T::AccountId, + reward_kind: T::RewardKind, + alternative_beneficiary: Option>, + ) -> DispatchResult { + RelayerRewards::::try_mutate_exists( + &relayer, + reward_kind, + |maybe_reward| -> DispatchResult { + let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; + T::PaymentProcedure::pay_reward( + &relayer, + reward_kind, + reward, + alternative_beneficiary.clone(), + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to pay ({:?} / {:?}) rewards to {:?}(alternative beneficiary: {:?}), error: {:?}", + reward_kind, + reward, + relayer, + alternative_beneficiary, + e, + ); + Error::::FailedToPayReward + })?; + + Self::deposit_event(Event::::RewardPaid { + relayer: relayer.clone(), + reward_kind, + reward, + alternative_beneficiary, + }); + Ok(()) + }, + ) + } + /// Returns true if given relayer registration is active at current block. /// /// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that @@ -264,7 +306,7 @@ pub mod pallet { /// It may fail inside, but error is swallowed and we only log it. pub fn slash_and_deregister( relayer: &T::AccountId, - slash_destination: ExplicitOrAccountParams, + slash_destination: impl IdentifyAccount, ) { let registration = match RegisteredRelayers::::take(relayer) { Some(registration) => registration, @@ -278,10 +320,11 @@ pub mod pallet { return }, }; + let slash_destination = slash_destination.into_account(); match T::StakeAndSlash::repatriate_reserved( relayer, - slash_destination.clone(), + &slash_destination, registration.stake, ) { Ok(failed_to_slash) if failed_to_slash.is_zero() => { @@ -307,8 +350,8 @@ pub mod pallet { Err(e) => { // TODO: document this. Where? - // it may fail if there's no beneficiary account. For us it means that this - // account must exists before we'll deploy the bridge + // it may fail if there's no beneficiary account. For us, it means that this + // account must exist before we'll deploy the bridge log::debug!( target: crate::LOG_TARGET, "Failed to slash relayer account {:?}: {:?}. Maybe beneficiary account doesn't exist? \ @@ -324,8 +367,8 @@ pub mod pallet { } /// Register reward for given relayer. - pub fn register_relayer_reward( - rewards_account_params: RewardsAccountParams, + pub(crate) fn register_relayer_reward( + reward_kind: T::RewardKind, relayer: &T::AccountId, reward: T::Reward, ) { @@ -335,7 +378,7 @@ pub mod pallet { RelayerRewards::::mutate( relayer, - rewards_account_params, + reward_kind, |old_reward: &mut Option| { let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); *old_reward = Some(new_reward); @@ -344,13 +387,13 @@ pub mod pallet { target: crate::LOG_TARGET, "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", relayer, - rewards_account_params, + reward_kind, new_reward, ); Self::deposit_event(Event::::RewardRegistered { relayer: relayer.clone(), - rewards_account_params, + reward_kind, reward, }); }, @@ -362,21 +405,21 @@ pub mod pallet { , - T::Reward, + T::Balance, >>::RequiredRegistrationLease::get() } /// Return required stake. - pub(crate) fn required_stake() -> T::Reward { + pub(crate) fn required_stake() -> T::Balance { , - T::Reward, + T::Balance, >>::RequiredStake::get() } /// `Unreserve` given amount on relayer account. - fn do_unreserve(relayer: &T::AccountId, amount: T::Reward) -> DispatchResult { + fn do_unreserve(relayer: &T::AccountId, amount: T::Balance) -> DispatchResult { let failed_to_unreserve = T::StakeAndSlash::unreserve(relayer, amount); if !failed_to_unreserve.is_zero() { log::trace!( @@ -401,8 +444,8 @@ pub mod pallet { RewardRegistered { /// Relayer account that can claim reward. relayer: T::AccountId, - /// Relayer can claim reward from this account. - rewards_account_params: RewardsAccountParams, + /// Relayer can claim this kind of reward. + reward_kind: T::RewardKind, /// Reward amount. reward: T::Reward, }, @@ -410,17 +453,19 @@ pub mod pallet { RewardPaid { /// Relayer account that has been rewarded. relayer: T::AccountId, - /// Relayer has received reward from this account. - rewards_account_params: RewardsAccountParams, + /// Relayer has received reward of this kind. + reward_kind: T::RewardKind, /// Reward amount. reward: T::Reward, + /// Alternative beneficiary. + alternative_beneficiary: Option>, }, /// Relayer registration has been added or updated. RegistrationUpdated { /// Relayer account that has been registered. relayer: T::AccountId, /// Relayer registration. - registration: Registration, T::Reward>, + registration: Registration, T::Balance>, }, /// Relayer has been `deregistered`. Deregistered { @@ -432,7 +477,7 @@ pub mod pallet { /// Relayer account that has been `deregistered`. relayer: T::AccountId, /// Registration that was removed. - registration: Registration, T::Reward>, + registration: Registration, T::Balance>, }, } @@ -482,23 +527,29 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - Registration, T::Reward>, + Registration, T::Balance>, OptionQuery, >; } +/// Implementation of `RewardLedger` for the pallet. +impl, I: 'static, RewardKind, Reward> RewardLedger + for Pallet +where + RewardKind: Into, + Reward: Into, +{ + fn register_reward(relayer: &T::AccountId, reward_kind: RewardKind, reward: Reward) { + Self::register_relayer_reward(reward_kind.into(), relayer, reward.into()); + } +} + #[cfg(test)] mod tests { use super::*; - use bp_messages::LaneIdType; use mock::{RuntimeEvent as TestEvent, *}; - use crate::Event::{RewardPaid, RewardRegistered}; - use bp_relayers::RewardsAccountOwner; - use frame_support::{ - assert_noop, assert_ok, - traits::fungible::{Inspect, Mutate}, - }; + use frame_support::{assert_noop, assert_ok, traits::fungible::Mutate}; use frame_system::{EventRecord, Pallet as System, Phase}; use sp_runtime::DispatchError; @@ -523,9 +574,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::BridgeRelayers(RewardRegistered { + event: TestEvent::BridgeRelayers(Event::RewardRegistered { relayer: REGULAR_RELAYER, - rewards_account_params: test_reward_account_param(), + reward_kind: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -602,10 +653,11 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::BridgeRelayers(RewardPaid { + event: TestEvent::BridgeRelayers(Event::RewardPaid { relayer: REGULAR_RELAYER, - rewards_account_params: test_reward_account_param(), - reward: 100 + reward_kind: test_reward_account_param(), + reward: 100, + alternative_beneficiary: None, }), topics: vec![], }), @@ -614,41 +666,39 @@ mod tests { } #[test] - fn pay_reward_from_account_actually_pays_reward() { - type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = - bp_relayers::PayRewardFromAccount; - + fn relayer_can_claim_reward_to() { run_test(|| { - let in_lane_0 = RewardsAccountParams::new( - TestLaneIdType::try_new(1, 2).unwrap(), - *b"test", - RewardsAccountOwner::ThisChain, - ); - let out_lane_1 = RewardsAccountParams::new( - TestLaneIdType::try_new(1, 3).unwrap(), - *b"test", - RewardsAccountOwner::BridgedChain, - ); - - let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); - let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); - - Balances::mint_into(&in_lane0_rewards_account, 100).unwrap(); - Balances::mint_into(&out_lane1_rewards_account, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 0); - - PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 100); - - PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); - assert_eq!(Balances::balance(&1), 200); + get_ready_for_events(); + + RelayerRewards::::insert( + REGULAR_RELAYER, + test_reward_account_param(), + 100, + ); + assert_ok!(Pallet::::claim_rewards_to( + RuntimeOrigin::signed(REGULAR_RELAYER), + test_reward_account_param(), + REGULAR_RELAYER2, + )); + assert_eq!( + RelayerRewards::::get(REGULAR_RELAYER, test_reward_account_param()), + None + ); + + // Check if the `RewardPaid` event was emitted. + assert_eq!( + System::::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: TestEvent::BridgeRelayers(Event::RewardPaid { + relayer: REGULAR_RELAYER, + reward_kind: test_reward_account_param(), + reward: 100, + alternative_beneficiary: Some(REGULAR_RELAYER2), + }), + topics: vec![], + }), + ); }); } diff --git a/bridges/modules/relayers/src/migration.rs b/bridges/modules/relayers/src/migration.rs index 8bf473b300c2a..c4fae102cc7f6 100644 --- a/bridges/modules/relayers/src/migration.rs +++ b/bridges/modules/relayers/src/migration.rs @@ -16,13 +16,14 @@ //! A module that is responsible for migration of storage. +use alloc::vec::Vec; use frame_support::{ traits::{Get, StorageVersion}, weights::Weight, }; /// The in-code storage version. -pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); /// This module contains data structures that are valid for the initial state of `0`. /// (used with v1 migration). @@ -31,10 +32,10 @@ pub mod v0 { use bp_relayers::RewardsAccountOwner; use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; + use core::marker::PhantomData; use frame_support::{pallet_prelude::OptionQuery, Blake2_128Concat, Identity}; use scale_info::TypeInfo; use sp_runtime::traits::AccountIdConversion; - use sp_std::marker::PhantomData; /// Structure used to identify the account that pays a reward to the relayer. #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] @@ -82,20 +83,20 @@ pub mod v0 { type Value = Reward; } - pub(crate) type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< + pub(crate) type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< ::AccountId, >::Reward, - >::LaneId, + LaneId, >; #[frame_support::storage_alias] - pub(crate) type RelayerRewards, I: 'static> = StorageDoubleMap< + pub(crate) type RelayerRewards, I: 'static, LaneId> = StorageDoubleMap< Pallet, - as StorageDoubleMapKeyProvider>::Hasher1, - as StorageDoubleMapKeyProvider>::Key1, - as StorageDoubleMapKeyProvider>::Hasher2, - as StorageDoubleMapKeyProvider>::Key2, - as StorageDoubleMapKeyProvider>::Value, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, OptionQuery, >; @@ -119,27 +120,103 @@ pub mod v0 { pub mod v1 { use super::*; use crate::{Config, Pallet}; + use bp_messages::LaneIdType; use bp_relayers::RewardsAccountParams; - use frame_support::traits::UncheckedOnRuntimeUpgrade; - use sp_std::marker::PhantomData; + use bp_runtime::StorageDoubleMapKeyProvider; + use codec::{Codec, EncodeLike}; + use core::marker::PhantomData; + use frame_support::{ + pallet_prelude::OptionQuery, traits::UncheckedOnRuntimeUpgrade, Blake2_128Concat, Identity, + }; + use sp_arithmetic::traits::Zero; - #[cfg(feature = "try-runtime")] - use crate::RelayerRewards; + pub(crate) struct RelayerRewardsKeyProvider( + PhantomData<(AccountId, Reward, LaneId)>, + ); + + impl StorageDoubleMapKeyProvider + for RelayerRewardsKeyProvider + where + AccountId: 'static + Codec + EncodeLike + Send + Sync, + Reward: 'static + Codec + EncodeLike + Send + Sync, + LaneId: Codec + EncodeLike + Send + Sync, + { + const MAP_NAME: &'static str = "RelayerRewards"; + + type Hasher1 = Blake2_128Concat; + type Key1 = AccountId; + type Hasher2 = Identity; + type Key2 = v1::RewardsAccountParams; + type Value = Reward; + } + + pub(crate) type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< + ::AccountId, + >::Reward, + LaneId, + >; + + #[frame_support::storage_alias] + pub(crate) type RelayerRewards, I: 'static, LaneId> = StorageDoubleMap< + Pallet, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, + OptionQuery, + >; + + // Copy of `Pallet::::register_relayer_reward` compatible with v1. + fn register_relayer_reward_for_v1< + T: Config, + I: 'static, + LaneId: LaneIdType + Send + Sync, + >( + rewards_account_params: v1::RewardsAccountParams, + relayer: &T::AccountId, + reward: T::Reward, + ) { + use sp_runtime::Saturating; + + if reward.is_zero() { + return + } + + v1::RelayerRewards::::mutate( + relayer, + rewards_account_params, + |old_reward: &mut Option| { + let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); + *old_reward = Some(new_reward); + + log::trace!( + target: crate::LOG_TARGET, + "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", + relayer, + rewards_account_params, + new_reward, + ); + }, + ); + } /// Migrates the pallet storage to v1. - pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I)>); + pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I, LaneId)>); #[cfg(feature = "try-runtime")] const LOG_TARGET: &str = "runtime::bridge-relayers-migration"; - impl, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1 { + impl, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade + for UncheckedMigrationV0ToV1 + { fn on_runtime_upgrade() -> Weight { let mut weight = T::DbWeight::get().reads(1); // list all rewards (we cannot do this as one step because of `drain` limitation) let mut rewards_to_migrate = - sp_std::vec::Vec::with_capacity(v0::RelayerRewards::::iter().count()); - for (key1, key2, reward) in v0::RelayerRewards::::drain() { + Vec::with_capacity(v0::RelayerRewards::::iter().count()); + for (key1, key2, reward) in v0::RelayerRewards::::drain() { rewards_to_migrate.push((key1, key2, reward)); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); } @@ -150,7 +227,7 @@ pub mod v1 { let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2; // re-register reward - Pallet::::register_relayer_reward( + register_relayer_reward_for_v1::( v1::RewardsAccountParams::new(lane_id, bridged_chain_id, owner), &key1, reward, @@ -162,18 +239,18 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { use codec::Encode; use frame_support::BoundedBTreeMap; use sp_runtime::traits::ConstU32; // collect actual rewards let mut rewards: BoundedBTreeMap< - (T::AccountId, T::LaneId), + (T::AccountId, LaneId), T::Reward, ConstU32<{ u32::MAX }>, > = BoundedBTreeMap::new(); - for (key1, key2, reward) in v0::RelayerRewards::::iter() { + for (key1, key2, reward) in v0::RelayerRewards::::iter() { log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?} - {reward:?}"); rewards = rewards .try_mutate(|inner| { @@ -190,24 +267,24 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::DispatchError> { use codec::Decode; use frame_support::BoundedBTreeMap; use sp_runtime::traits::ConstU32; let rewards_before: BoundedBTreeMap< - (T::AccountId, T::LaneId), + (T::AccountId, LaneId), T::Reward, ConstU32<{ u32::MAX }>, > = Decode::decode(&mut &state[..]).unwrap(); // collect migrated rewards let mut rewards_after: BoundedBTreeMap< - (T::AccountId, T::LaneId), + (T::AccountId, LaneId), T::Reward, ConstU32<{ u32::MAX }>, > = BoundedBTreeMap::new(); - for (key1, key2, reward) in v1::RelayerRewards::::iter() { + for (key1, key2, reward) in v1::RelayerRewards::::iter() { log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}"); rewards_after = rewards_after .try_mutate(|inner| { @@ -233,10 +310,140 @@ pub mod v1 { /// [`UncheckedMigrationV0ToV1`] wrapped in a /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the /// migration is only performed when on-chain version is 0. - pub type MigrationToV1 = frame_support::migrations::VersionedMigration< + pub type MigrationToV1 = frame_support::migrations::VersionedMigration< 0, 1, - UncheckedMigrationV0ToV1, + UncheckedMigrationV0ToV1, + Pallet, + ::DbWeight, + >; +} + +/// The pallet in version 1 only supported rewards collected under the key of +/// `RewardsAccountParams`. This migration essentially converts existing `RewardsAccountParams` keys +/// to the generic type `T::RewardKind`. +pub mod v2 { + use super::*; + #[cfg(feature = "try-runtime")] + use crate::RelayerRewards; + use crate::{Config, Pallet}; + use bp_messages::LaneIdType; + use bp_relayers::RewardsAccountParams; + use core::marker::PhantomData; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + + /// Migrates the pallet storage to v2. + pub struct UncheckedMigrationV1ToV2(PhantomData<(T, I, LaneId)>); + + #[cfg(feature = "try-runtime")] + const LOG_TARGET: &str = "runtime::bridge-relayers-migration"; + + impl, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade + for UncheckedMigrationV1ToV2 + where + >::RewardKind: From>, + { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // list all rewards (we cannot do this as one step because of `drain` limitation) + let mut rewards_to_migrate = + Vec::with_capacity(v1::RelayerRewards::::iter().count()); + for (key1, key2, reward) in v1::RelayerRewards::::drain() { + rewards_to_migrate.push((key1, key2, reward)); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } + + // re-register rewards with new format. + for (key1, key2, reward) in rewards_to_migrate { + // convert old key to the new + let new_key2: T::RewardKind = key2.into(); + + // re-register reward (drained above) + Pallet::::register_relayer_reward(new_key2, &key1, reward); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + use codec::Encode; + use frame_support::BoundedBTreeMap; + use sp_runtime::traits::ConstU32; + + // collect actual rewards + let mut rewards: BoundedBTreeMap< + (T::AccountId, Vec), + T::Reward, + ConstU32<{ u32::MAX }>, + > = BoundedBTreeMap::new(); + for (key1, key2, reward) in v1::RelayerRewards::::iter() { + let new_key2: T::RewardKind = key2.into(); + log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?}->{new_key2:?} - {reward:?}"); + rewards = rewards + .try_mutate(|inner| { + inner + .entry((key1.clone(), new_key2.encode())) + .and_modify(|value| *value += reward) + .or_insert(reward); + }) + .unwrap(); + } + log::info!(target: LOG_TARGET, "Found total rewards to migrate: {rewards:?}"); + + Ok(rewards.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::DispatchError> { + use codec::{Decode, Encode}; + use frame_support::BoundedBTreeMap; + use sp_runtime::traits::ConstU32; + + let rewards_before: BoundedBTreeMap< + (T::AccountId, Vec), + T::Reward, + ConstU32<{ u32::MAX }>, + > = Decode::decode(&mut &state[..]).unwrap(); + + // collect migrated rewards + let mut rewards_after: BoundedBTreeMap< + (T::AccountId, Vec), + T::Reward, + ConstU32<{ u32::MAX }>, + > = BoundedBTreeMap::new(); + for (key1, key2, reward) in v2::RelayerRewards::::iter() { + log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}"); + rewards_after = rewards_after + .try_mutate(|inner| { + inner + .entry((key1.clone(), key2.encode())) + .and_modify(|value| *value += reward) + .or_insert(reward); + }) + .unwrap(); + } + log::info!(target: LOG_TARGET, "Found total migrated rewards: {rewards_after:?}"); + + frame_support::ensure!( + rewards_before == rewards_after, + "The rewards were not migrated correctly!." + ); + + log::info!(target: LOG_TARGET, "migrated all."); + Ok(()) + } + } + + /// [`UncheckedMigrationV1ToV2`] wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the + /// migration is only performed when on-chain version is 1. + pub type MigrationToV2 = frame_support::migrations::VersionedMigration< + 1, + 2, + UncheckedMigrationV1ToV2, Pallet, ::DbWeight, >; diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 7dc213249379d..73a7da7920f91 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -35,9 +35,9 @@ use frame_support::{ weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, }; use pallet_transaction_payment::Multiplier; -use sp_core::{ConstU64, ConstU8, H256}; +use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, ConstU32}, + traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8}, BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; @@ -81,6 +81,8 @@ pub type TestLaneIdType = HashedLaneId; pub fn test_lane_id() -> TestLaneIdType { TestLaneIdType::try_new(1, 2).unwrap() } +/// Reward measurement type. +pub type Reward = u64; /// Underlying chain of `ThisChain`. pub struct ThisUnderlyingChain; @@ -280,34 +282,51 @@ impl pallet_bridge_messages::Config for TestRuntime { impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = ThisChainBalance; + type Reward = Reward; + type RewardKind = RewardsAccountParams>; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; + type Balance = ThisChainBalance; type WeightInfo = (); - type LaneId = TestLaneIdType; } #[cfg(feature = "runtime-benchmarks")] impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { + fn bench_reward_kind() -> Self::RewardKind { + RewardsAccountParams::new( + TestLaneIdType::default(), + *b"test", + RewardsAccountOwner::ThisChain, + ) + } + fn prepare_rewards_account( - account_params: RewardsAccountParams, + account_params: RewardsAccountParams, reward: Self::Reward, - ) { - let rewards_account = bp_relayers::PayRewardFromAccount::< + ) -> Option { + let rewards_account = PayRewardFromAccount::< Balances, ThisChainAccountId, - Self::LaneId, + TestLaneIdType, + Reward, >::rewards_account(account_params); - Self::deposit_account(rewards_account, reward); + Self::deposit_account(rewards_account, reward.into()); + + Some(REGULAR_RELAYER2) } - fn deposit_account(account: Self::AccountId, balance: Self::Reward) { - Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); + fn deposit_account(account: Self::AccountId, balance: Self::Balance) { + frame_support::assert_ok!(Balances::mint_into( + &account, + balance.saturating_add(ExistentialDeposit::get()) + )); } } /// Regular relayer that may receive rewards. pub const REGULAR_RELAYER: ThisChainAccountId = 1; +/// Regular relayer that may receive rewards. +pub const REGULAR_RELAYER2: ThisChainAccountId = 3; /// Relayer that can't receive rewards. pub const FAILING_RELAYER: ThisChainAccountId = 2; @@ -320,18 +339,23 @@ pub struct TestPaymentProcedure; impl TestPaymentProcedure { pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId { - PayRewardFromAccount::<(), ThisChainAccountId, TestLaneIdType>::rewards_account(params) + PayRewardFromAccount::<(), ThisChainAccountId, TestLaneIdType, Reward>::rewards_account( + params, + ) } } -impl PaymentProcedure for TestPaymentProcedure { +impl PaymentProcedure, Reward> + for TestPaymentProcedure +{ type Error = (); - type LaneId = TestLaneIdType; + type AlternativeBeneficiary = ThisChainAccountId; fn pay_reward( relayer: &ThisChainAccountId, - _lane_id: RewardsAccountParams, - _reward: ThisChainBalance, + _reward_kind: RewardsAccountParams, + _reward: Reward, + _alternative_beneficiary: Option, ) -> Result<(), Self::Error> { match *relayer { FAILING_RELAYER => Err(()), diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs index 5af0d8f9dfbf4..32238275c722f 100644 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ b/bridges/modules/relayers/src/payment_adapter.rs @@ -14,10 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Code that allows relayers pallet to be used as a payment mechanism for the messages pallet. +//! Code that allows relayers pallet to be used as a payment mechanism for +//! the `pallet-bridge-messages` pallet using `RewardsAccountParams`. use crate::{Config, Pallet}; +use alloc::collections::vec_deque::VecDeque; use bp_messages::{ source_chain::{DeliveryConfirmationPayments, RelayersRewards}, MessageNonce, @@ -25,13 +27,13 @@ use bp_messages::{ pub use bp_relayers::PayRewardFromAccount; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::Chain; +use core::{marker::PhantomData, ops::RangeInclusive}; use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; use pallet_bridge_messages::LaneIdOf; use sp_arithmetic::traits::{Saturating, Zero}; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism -/// for the messages pallet. +/// for the `pallet-bridge-messages` pallet and using `RewardsAccountParams`. pub struct DeliveryConfirmationPaymentsAdapter( PhantomData<(T, MI, RI, DeliveryReward)>, ); @@ -39,10 +41,11 @@ pub struct DeliveryConfirmationPaymentsAdapter( impl DeliveryConfirmationPayments> for DeliveryConfirmationPaymentsAdapter where - T: Config + pallet_bridge_messages::Config>::LaneId>, + T: Config + pallet_bridge_messages::Config, MI: 'static, RI: 'static, DeliveryReward: Get, + >::RewardKind: From>>, { type Error = &'static str; @@ -56,7 +59,7 @@ where bp_messages::calc_relayers_rewards::(messages_relayers, received_range); let rewarded_relayers = relayers_rewards.len(); - register_relayers_rewards::( + register_relayers_rewards::( confirmation_relayer, relayers_rewards, RewardsAccountParams::new( @@ -72,12 +75,18 @@ where } // Update rewards to given relayers, optionally rewarding confirmation relayer. -fn register_relayers_rewards, I: 'static>( +fn register_relayers_rewards< + T: Config + pallet_bridge_messages::Config, + RI: 'static, + MI: 'static, +>( confirmation_relayer: &T::AccountId, relayers_rewards: RelayersRewards, - lane_id: RewardsAccountParams, + lane_id: RewardsAccountParams>, delivery_fee: T::Reward, -) { +) where + >::RewardKind: From>>, +{ // reward every relayer except `confirmation_relayer` let mut confirmation_relayer_reward = T::Reward::zero(); for (relayer, messages) in relayers_rewards { @@ -86,7 +95,7 @@ fn register_relayers_rewards, I: 'static>( let relayer_reward = T::Reward::saturated_from(messages).saturating_mul(delivery_fee); if relayer != *confirmation_relayer { - Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); + Pallet::::register_relayer_reward(lane_id.into(), &relayer, relayer_reward); } else { confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(relayer_reward); @@ -94,8 +103,8 @@ fn register_relayers_rewards, I: 'static>( } // finally - pay reward to confirmation relayer - Pallet::::register_relayer_reward( - lane_id, + Pallet::::register_relayer_reward( + lane_id.into(), confirmation_relayer, confirmation_relayer_reward, ); @@ -105,6 +114,12 @@ fn register_relayers_rewards, I: 'static>( mod tests { use super::*; use crate::{mock::*, RelayerRewards}; + use bp_messages::LaneIdType; + use bp_relayers::PaymentProcedure; + use frame_support::{ + assert_ok, + traits::fungible::{Inspect, Mutate}, + }; const RELAYER_1: ThisChainAccountId = 1; const RELAYER_2: ThisChainAccountId = 2; @@ -117,7 +132,7 @@ mod tests { #[test] fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { run_test(|| { - register_relayers_rewards::( + register_relayers_rewards::( &RELAYER_2, relayers_rewards(), test_reward_account_param(), @@ -138,7 +153,7 @@ mod tests { #[test] fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { run_test(|| { - register_relayers_rewards::( + register_relayers_rewards::( &RELAYER_3, relayers_rewards(), test_reward_account_param(), @@ -159,4 +174,52 @@ mod tests { ); }); } + + #[test] + fn pay_reward_from_account_actually_pays_reward() { + type Balances = pallet_balances::Pallet; + type PayLaneRewardFromAccount = + PayRewardFromAccount; + + run_test(|| { + let in_lane_0 = RewardsAccountParams::new( + TestLaneIdType::try_new(1, 2).unwrap(), + *b"test", + RewardsAccountOwner::ThisChain, + ); + let out_lane_1 = RewardsAccountParams::new( + TestLaneIdType::try_new(1, 3).unwrap(), + *b"test", + RewardsAccountOwner::BridgedChain, + ); + + let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); + let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); + + assert_ok!(Balances::mint_into(&in_lane0_rewards_account, 200)); + assert_ok!(Balances::mint_into(&out_lane1_rewards_account, 100)); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 200); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); + assert_eq!(Balances::balance(&1), 0); + assert_eq!(Balances::balance(&2), 0); + + assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, None)); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); + assert_eq!(Balances::balance(&1), 100); + assert_eq!(Balances::balance(&2), 0); + + assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100, None)); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); + assert_eq!(Balances::balance(&1), 200); + assert_eq!(Balances::balance(&2), 0); + + assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, Some(2))); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); + assert_eq!(Balances::balance(&1), 200); + assert_eq!(Balances::balance(&2), 100); + }); + } } diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs index 1792f0be8316a..9b5ae1739520c 100644 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ b/bridges/modules/relayers/src/stake_adapter.rs @@ -17,11 +17,11 @@ //! Code that allows `NamedReservableCurrency` to be used as a `StakeAndSlash` //! mechanism of the relayers pallet. -use bp_relayers::{ExplicitOrAccountParams, PayRewardFromAccount, StakeAndSlash}; -use codec::{Codec, Decode, Encode}; +use bp_relayers::StakeAndSlash; +use codec::Codec; +use core::{fmt::Debug, marker::PhantomData}; use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency}; use sp_runtime::{traits::Get, DispatchError, DispatchResult}; -use sp_std::{fmt::Debug, marker::PhantomData}; /// `StakeAndSlash` that works with `NamedReservableCurrency` and uses named /// reservations. @@ -53,20 +53,15 @@ where Currency::unreserve_named(&ReserveId::get(), relayer, amount) } - fn repatriate_reserved( + fn repatriate_reserved( relayer: &AccountId, - beneficiary: ExplicitOrAccountParams, + beneficiary: &AccountId, amount: Currency::Balance, ) -> Result { - let beneficiary_account = match beneficiary { - ExplicitOrAccountParams::Explicit(account) => account, - ExplicitOrAccountParams::Params(params) => - PayRewardFromAccount::<(), AccountId, LaneId>::rewards_account(params), - }; Currency::repatriate_reserved_named( &ReserveId::get(), relayer, - &beneficiary_account, + &beneficiary, amount, BalanceStatus::Free, ) @@ -77,8 +72,10 @@ where mod tests { use super::*; use crate::mock::*; + use bp_relayers::ExplicitOrAccountParams; use frame_support::traits::fungible::Mutate; + use sp_runtime::traits::IdentifyAccount; fn test_stake() -> ThisChainBalance { Stake::get() @@ -139,7 +136,7 @@ mod tests { assert_eq!( TestStakeAndSlash::repatriate_reserved( &1, - ExplicitOrAccountParams::Params(beneficiary), + &(ExplicitOrAccountParams::Params(beneficiary).into_account()), test_stake() ), Ok(test_stake()) @@ -155,7 +152,7 @@ mod tests { assert_eq!( TestStakeAndSlash::repatriate_reserved( &2, - ExplicitOrAccountParams::Params(beneficiary), + &(ExplicitOrAccountParams::Params(beneficiary).into_account()), test_stake() ), Ok(test_stake() - test_stake() / 3) @@ -171,7 +168,7 @@ mod tests { assert_eq!( TestStakeAndSlash::repatriate_reserved( &3, - ExplicitOrAccountParams::Params(beneficiary), + &(ExplicitOrAccountParams::Params(beneficiary).into_account()), test_stake() ), Ok(0) @@ -193,7 +190,7 @@ mod tests { TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); assert!(TestStakeAndSlash::repatriate_reserved( &3, - ExplicitOrAccountParams::Params(beneficiary), + &(ExplicitOrAccountParams::Params(beneficiary).into_account()), test_stake() ) .is_err()); diff --git a/bridges/modules/relayers/src/weights.rs b/bridges/modules/relayers/src/weights.rs index c2c065b0c0a27..6acaab7b78c3a 100644 --- a/bridges/modules/relayers/src/weights.rs +++ b/bridges/modules/relayers/src/weights.rs @@ -42,15 +42,16 @@ #![allow(unused_imports)] #![allow(missing_docs)] +use core::marker::PhantomData; use frame_support::{ traits::Get, weights::{constants::RocksDbWeight, Weight}, }; -use sp_std::marker::PhantomData; /// Weight functions needed for pallet_bridge_relayers. pub trait WeightInfo { fn claim_rewards() -> Weight; + fn claim_rewards_to() -> Weight; fn register() -> Weight; fn deregister() -> Weight; fn slash_and_deregister() -> Weight; @@ -85,6 +86,29 @@ impl WeightInfo for BridgeWeight { .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:0) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: + /// MaxEncodedLen) + fn claim_rewards_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `294` + // Estimated: `8592` + // Minimum execution time: 77_614 nanoseconds. + Weight::from_parts(79_987_000, 8592) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) /// /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, @@ -184,6 +208,29 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:0) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: + /// MaxEncodedLen) + fn claim_rewards_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `294` + // Estimated: `8592` + // Minimum execution time: 77_614 nanoseconds. + Weight::from_parts(79_987_000, 8592) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) /// /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, diff --git a/bridges/modules/relayers/src/weights_ext.rs b/bridges/modules/relayers/src/weights_ext.rs index 9cd25c47c3782..e1d6dcc4a8ee5 100644 --- a/bridges/modules/relayers/src/weights_ext.rs +++ b/bridges/modules/relayers/src/weights_ext.rs @@ -23,13 +23,13 @@ use frame_support::pallet_prelude::Weight; /// Extended weight info. pub trait WeightInfoExt: WeightInfo { /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery call, - /// if `RefundBridgedParachainMessages` signed extension is deployed at runtime level. + /// if `BridgeRelayersTransactionExtension` signed extension is deployed at runtime level. fn receive_messages_proof_overhead_from_runtime() -> Weight { Self::slash_and_deregister().max(Self::register_relayer_reward()) } /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery - /// confirmation call, if `RefundBridgedParachainMessages` signed extension is deployed at + /// confirmation call, if `BridgeRelayersTransactionExtension` signed extension is deployed at /// runtime level. fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { Self::register_relayer_reward() diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index faa4cb1776294..8f723220e125d 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -90,38 +90,44 @@ impl TypeId for RewardsAccountParams { } /// Reward payment procedure. -pub trait PaymentProcedure { +pub trait PaymentProcedure { /// Error that may be returned by the procedure. type Error: Debug; - /// Lane identifier type. - type LaneId: Decode + Encode; - /// Pay reward to the relayer from the account with provided params. + /// Type parameter used to identify the alternative beneficiaries eligible to receive rewards. + type AlternativeBeneficiary: Clone + Debug + Decode + Encode + Eq + TypeInfo; + + /// Pay reward to the relayer (or alternative beneficiary if provided) from the account with + /// provided params. fn pay_reward( relayer: &Relayer, - rewards_account_params: RewardsAccountParams, + reward_kind: RewardKind, reward: Reward, + alternative_beneficiary: Option, ) -> Result<(), Self::Error>; } -impl PaymentProcedure for () { +impl PaymentProcedure for () { type Error = &'static str; - type LaneId = (); + type AlternativeBeneficiary = (); fn pay_reward( _: &Relayer, - _: RewardsAccountParams, + _: RewardKind, _: Reward, + _: Option, ) -> Result<(), Self::Error> { Ok(()) } } -/// Reward payment procedure that does `balances::transfer` call from the account, derived from -/// given params. -pub struct PayRewardFromAccount(PhantomData<(T, Relayer, LaneId)>); +/// Reward payment procedure that executes a `balances::transfer` call from the account +/// derived from the given `RewardsAccountParams` to the relayer or an alternative beneficiary. +pub struct PayRewardFromAccount( + PhantomData<(T, Relayer, LaneId, Reward)>, +); -impl PayRewardFromAccount +impl PayRewardFromAccount where Relayer: Decode + Encode, LaneId: Decode + Encode, @@ -132,25 +138,27 @@ where } } -impl PaymentProcedure - for PayRewardFromAccount +impl PaymentProcedure, Reward> + for PayRewardFromAccount where T: frame_support::traits::fungible::Mutate, - Relayer: Decode + Encode + Eq, + T::Balance: From, + Relayer: Clone + Debug + Decode + Encode + Eq + TypeInfo, LaneId: Decode + Encode, { type Error = sp_runtime::DispatchError; - type LaneId = LaneId; + type AlternativeBeneficiary = Relayer; fn pay_reward( relayer: &Relayer, - rewards_account_params: RewardsAccountParams, - reward: T::Balance, + reward_kind: RewardsAccountParams, + reward: Reward, + alternative_beneficiary: Option, ) -> Result<(), Self::Error> { T::transfer( - &Self::rewards_account(rewards_account_params), - relayer, - reward, + &Self::rewards_account(reward_kind), + alternative_beneficiary.as_ref().unwrap_or(relayer), + reward.into(), Preservation::Expendable, ) .map(drop) @@ -159,26 +167,35 @@ where /// Can be used to access the runtime storage key within the `RelayerRewards` map of the relayers /// pallet. -pub struct RelayerRewardsKeyProvider( - PhantomData<(AccountId, Reward, LaneId)>, +pub struct RelayerRewardsKeyProvider( + PhantomData<(AccountId, RewardKind, Reward)>, ); -impl StorageDoubleMapKeyProvider - for RelayerRewardsKeyProvider +impl StorageDoubleMapKeyProvider + for RelayerRewardsKeyProvider where AccountId: 'static + Codec + EncodeLike + Send + Sync, Reward: 'static + Codec + EncodeLike + Send + Sync, - LaneId: Codec + EncodeLike + Send + Sync, + RewardKind: Codec + EncodeLike + Send + Sync, { const MAP_NAME: &'static str = "RelayerRewards"; type Hasher1 = Blake2_128Concat; type Key1 = AccountId; type Hasher2 = Identity; - type Key2 = RewardsAccountParams; + type Key2 = RewardKind; type Value = Reward; } +/// A trait defining a reward ledger, which tracks rewards that can be later claimed. +/// +/// This ledger allows registering rewards for a relayer, categorized by a specific `RewardKind`. +/// The registered rewards can be claimed later through an appropriate payment procedure. +pub trait RewardLedger { + /// Registers a reward for a given relayer. + fn register_reward(relayer: &Relayer, reward_kind: RewardKind, reward: Reward); +} + #[cfg(test)] mod tests { use super::*; @@ -188,7 +205,7 @@ mod tests { #[test] fn different_lanes_are_using_different_accounts() { assert_eq!( - PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account( RewardsAccountParams::new( HashedLaneId::try_new(1, 2).unwrap(), *b"test", @@ -200,7 +217,7 @@ mod tests { ); assert_eq!( - PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account( RewardsAccountParams::new( HashedLaneId::try_new(1, 3).unwrap(), *b"test", @@ -215,7 +232,7 @@ mod tests { #[test] fn different_directions_are_using_different_accounts() { assert_eq!( - PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account( RewardsAccountParams::new( HashedLaneId::try_new(1, 2).unwrap(), *b"test", @@ -227,7 +244,7 @@ mod tests { ); assert_eq!( - PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account( RewardsAccountParams::new( HashedLaneId::try_new(1, 2).unwrap(), *b"test", @@ -303,6 +320,7 @@ mod tests { [u8; 32], [u8; 32], LegacyLaneId, + (), >::rewards_account(RewardsAccountParams::new( lane_id, *bridged_chain_id, diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs index d74ef18cf706b..b30e940455840 100644 --- a/bridges/primitives/relayers/src/registration.rs +++ b/bridges/primitives/relayers/src/registration.rs @@ -37,12 +37,12 @@ //! that guarantees that their transactions are valid. Such relayers get priority //! for free, but they risk to lose their stake. -use crate::RewardsAccountParams; +use crate::{PayRewardFromAccount, RewardsAccountParams}; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{Get, Zero}, + traits::{Get, IdentifyAccount, Zero}, DispatchError, DispatchResult, }; @@ -63,6 +63,20 @@ impl From> } } +impl IdentifyAccount + for ExplicitOrAccountParams +{ + type AccountId = AccountId; + + fn into_account(self) -> Self::AccountId { + match self { + ExplicitOrAccountParams::Explicit(account_id) => account_id, + ExplicitOrAccountParams::Params(params) => + PayRewardFromAccount::<(), AccountId, LaneId, ()>::rewards_account(params), + } + } +} + /// Relayer registration. #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] pub struct Registration { @@ -105,9 +119,9 @@ pub trait StakeAndSlash { /// `beneficiary`. /// /// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake. - fn repatriate_reserved( + fn repatriate_reserved( relayer: &AccountId, - beneficiary: ExplicitOrAccountParams, + beneficiary: &AccountId, amount: Balance, ) -> Result; } @@ -128,9 +142,9 @@ where Zero::zero() } - fn repatriate_reserved( + fn repatriate_reserved( _relayer: &AccountId, - _beneficiary: ExplicitOrAccountParams, + _beneficiary: &AccountId, _amount: Balance, ) -> Result { Ok(Zero::zero()) diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 9856f0d0237ea..68f0a503cfc77 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -23,7 +23,8 @@ use bp_runtime::{ Chain as ChainBase, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, TransactionEra, TransactionEraOf, UnderlyingChainProvider, }; -use codec::{Codec, Decode, Encode}; +use codec::{Codec, Decode, Encode, MaxEncodedLen}; +use frame_support::Parameter; use jsonrpsee::core::{DeserializeOwned, Serialize}; use num_traits::Zero; use sc_transaction_pool_api::TransactionStatus; @@ -31,7 +32,7 @@ use scale_info::TypeInfo; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{ generic::SignedBlock, - traits::{Block as BlockT, Member}, + traits::{AtLeast32BitUnsigned, Block as BlockT, Member}, ConsensusEngineId, EncodedJustification, }; use std::{fmt::Debug, time::Duration}; @@ -113,13 +114,6 @@ impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase /// Substrate-based chain with messaging support from minimal relay-client point of view. pub trait ChainWithMessages: Chain + ChainWithMessagesBase { - /// Name of the bridge relayers pallet (used in `construct_runtime` macro call) that is deployed - /// at some other chain to bridge with this `ChainWithMessages`. - /// - /// We assume that all chains that are bridging with this `ChainWithMessages` are using - /// the same name. - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str>; - /// Name of the `ToOutboundLaneApi::message_details` runtime API method. /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; @@ -141,6 +135,22 @@ pub trait ChainWithBalances: Chain { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey; } +/// Substrate-based chain with bridge relayers pallet as a reward ledger. +pub trait ChainWithRewards: Chain { + /// Name of the bridge relayers pallet (used in `construct_runtime` macro call). + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str>; + /// Type of relayer reward. + type Reward: AtLeast32BitUnsigned + Copy + Member + Parameter + MaxEncodedLen; + /// Reward discriminator type. + type RewardKind: Parameter + MaxEncodedLen + Send + Sync + Copy + Clone; + + /// Return runtime storage key for getting `reward_kind` of given account. + fn account_reward_storage_key( + account_id: &Self::AccountId, + reward_kind: impl Into, + ) -> StorageKey; +} + /// SCALE-encoded extrinsic. pub type EncodedExtrinsic = Vec; diff --git a/bridges/relays/client-substrate/src/lib.rs b/bridges/relays/client-substrate/src/lib.rs index 12a1c48c09c7a..10638093ca76a 100644 --- a/bridges/relays/client-substrate/src/lib.rs +++ b/bridges/relays/client-substrate/src/lib.rs @@ -34,10 +34,10 @@ use std::time::Duration; pub use crate::{ chain::{ AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, - ChainWithGrandpa, ChainWithMessages, ChainWithRuntimeVersion, ChainWithTransactions, - ChainWithUtilityPallet, FullRuntimeUtilityPallet, MockedRuntimeUtilityPallet, Parachain, - RelayChain, SignParam, SignedBlockOf, TransactionStatusOf, UnsignedTransaction, - UtilityPallet, + ChainWithGrandpa, ChainWithMessages, ChainWithRewards, ChainWithRuntimeVersion, + ChainWithTransactions, ChainWithUtilityPallet, FullRuntimeUtilityPallet, + MockedRuntimeUtilityPallet, Parachain, RelayChain, SignParam, SignedBlockOf, + TransactionStatusOf, UnsignedTransaction, UtilityPallet, }, client::{ is_ancient_block, rpc_with_caching as new, ChainRuntimeVersion, Client, diff --git a/bridges/relays/client-substrate/src/test_chain.rs b/bridges/relays/client-substrate/src/test_chain.rs index 991202e9874c7..8bdbea20e9a1b 100644 --- a/bridges/relays/client-substrate/src/test_chain.rs +++ b/bridges/relays/client-substrate/src/test_chain.rs @@ -80,7 +80,6 @@ impl ChainWithMessagesBase for TestChain { } impl ChainWithMessages for TestChain { - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestMessagesDetailsMethod"; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestFromMessagesDetailsMethod"; } diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs index bb6c689a76eb0..e815e092fe607 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs @@ -316,23 +316,16 @@ where })?; { let common = self.mut_base().mut_common(); - crate::messages::metrics::add_relay_balances_metrics::< - _, - Self::Right, - MessagesLaneIdOf, - >( - common.left.client.clone(), &common.metrics_params, &common.left.accounts, &lanes_l2r + crate::messages::metrics::add_relay_balances_metrics::<_>( + common.left.client.clone(), + &common.metrics_params, + &common.left.accounts, ) .await?; - crate::messages::metrics::add_relay_balances_metrics::< - _, - Self::Left, - MessagesLaneIdOf, - >( + crate::messages::metrics::add_relay_balances_metrics::<_>( common.right.client.clone(), &common.metrics_params, &common.right.accounts, - &lanes_r2l, ) .await?; } diff --git a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs index efe429701c41b..c6608b7ebf792 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs @@ -19,14 +19,13 @@ use crate::TaggedAccount; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::StorageDoubleMapKeyProvider; use codec::{Decode, EncodeLike}; use frame_system::AccountInfo; use messages_relay::Labeled; use pallet_balances::AccountData; use relay_substrate_client::{ - metrics::{FloatStorageValue, FloatStorageValueMetric}, - AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, Client, + metrics::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric}, + AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithRewards, Client, Error as SubstrateError, NonceOf, }; use relay_utils::metrics::{MetricsParams, StandaloneMetric}; @@ -35,15 +34,13 @@ use sp_runtime::{FixedPointNumber, FixedU128}; use std::{fmt::Debug, marker::PhantomData}; /// Add relay accounts balance metrics. -pub async fn add_relay_balances_metrics( +pub async fn add_relay_balances_metrics( client: impl Client, metrics: &MetricsParams, relay_accounts: &Vec>>, - lanes: &[LaneId], ) -> anyhow::Result<()> where BalanceOf: Into + std::fmt::Debug, - LaneId: Clone + Copy + Decode + EncodeLike + Send + Sync + Labeled, { if relay_accounts.is_empty() { return Ok(()) @@ -80,29 +77,42 @@ where format!("Balance of the {} relay account at the {}", account.tag(), C::NAME), )?; relay_account_balance_metric.register_and_spawn(&metrics.registry)?; + } + + Ok(()) +} + +/// Add relay accounts rewards metrics. +pub async fn add_relay_rewards_metrics( + client: impl Client, + metrics: &MetricsParams, + relay_accounts: &Vec>>, + lanes: &[LaneId], +) -> anyhow::Result<()> +where + C::Reward: Into + std::fmt::Debug, + C::RewardKind: From>, + LaneId: Clone + Copy + Decode + EncodeLike + Send + Sync + Labeled, +{ + if relay_accounts.is_empty() { + return Ok(()) + } - if let Some(relayers_pallet_name) = BC::WITH_CHAIN_RELAYERS_PALLET_NAME { + for account in relay_accounts { + if let Some(_) = C::WITH_CHAIN_RELAYERS_PALLET_NAME { for lane in lanes { FloatStorageValueMetric::new( - AccountBalance:: { token_decimals, _phantom: Default::default() }, + FixedU128OrOne, client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf, LaneId>::final_key( - relayers_pallet_name, - account.id(), - &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain), - ), + C::account_reward_storage_key(account.id(), RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain)), format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()), format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()), )?.register_and_spawn(&metrics.registry)?; FloatStorageValueMetric::new( - AccountBalance:: { token_decimals, _phantom: Default::default() }, + FixedU128OrOne, client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf, LaneId>::final_key( - relayers_pallet_name, - account.id(), - &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain), - ), + C::account_reward_storage_key(account.id(), RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain)), format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()), format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()), )?.register_and_spawn(&metrics.registry)?; @@ -143,34 +153,6 @@ where } } -/// Adapter for `FloatStorageValueMetric` to decode account free balance. -#[derive(Clone, Debug)] -struct AccountBalance { - token_decimals: u32, - _phantom: PhantomData, -} - -impl FloatStorageValue for AccountBalance -where - C: Chain, - BalanceOf: Into, -{ - type Value = FixedU128; - - fn decode( - &self, - maybe_raw_value: Option, - ) -> Result, SubstrateError> { - maybe_raw_value - .map(|raw_value| { - BalanceOf::::decode(&mut &raw_value.0[..]) - .map_err(SubstrateError::ResponseParseFailed) - .map(|balance| convert_to_token_balance(balance.into(), self.token_decimals)) - }) - .transpose() - } -} - /// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular /// tokens value. fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 { diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs index b4ee57ed7742e..a522515926b13 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs @@ -974,7 +974,6 @@ mod tests { } } impl relay_substrate_client::ChainWithMessages for ThisChain { - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = ""; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = ""; } @@ -1010,7 +1009,6 @@ mod tests { } } impl relay_substrate_client::ChainWithMessages for BridgedChain { - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = ""; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = ""; } diff --git a/bridges/testing/framework/js-helpers/relayer-rewards.js b/bridges/testing/framework/js-helpers/relayer-rewards.js index 5347c649604fc..3b74edc93bf26 100644 --- a/bridges/testing/framework/js-helpers/relayer-rewards.js +++ b/bridges/testing/framework/js-helpers/relayer-rewards.js @@ -3,16 +3,12 @@ async function run(nodeName, networkInfo, args) { const api = await zombie.connect(wsUri, userDefinedTypes); // TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later - const relayerAccountAddress = args[0]; - const laneId = args[1]; - const bridgedChainId = args[2]; - const relayerFundOwner = args[3]; - const expectedRelayerReward = BigInt(args[4]); + const relayerAccountAddress = args.relayerAccountAddress; + const reward_kind = args.rewardKind; + const expectedRelayerReward = BigInt(args.expectedRelayerReward); + console.log("Waiting rewards for relayerAccountAddress: " + relayerAccountAddress + " expecting minimal rewards at least: " + expectedRelayerReward + " for " + JSON.stringify(reward_kind)); while (true) { - const relayerReward = await api.query.bridgeRelayers.relayerRewards( - relayerAccountAddress, - { laneId: laneId, bridgedChainId: bridgedChainId, owner: relayerFundOwner } - ); + const relayerReward = await api.query.bridgeRelayers.relayerRewards(relayerAccountAddress, reward_kind); if (relayerReward.isSome) { const relayerRewardBalance = relayerReward.unwrap().toBigInt(); if (relayerRewardBalance > expectedRelayerReward) { diff --git a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl index b3cafc993e543..75becb2f02bc6 100644 --- a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl @@ -9,4 +9,4 @@ asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "auto-log reserve-t asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with '{ "accountAddress": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "expectedAssetBalance": 4800000000000, "expectedAssetId": { "parents": 2, "interior": { "X1": [{ "GlobalConsensus": { "ByGenesis": [100,8,222,119,55,197,156,35,136,144,83,58,242,88,150,162,194,6,8,216,179,128,187,1,2,154,203,57,39,129,6,62] } }] }}}' within 600 seconds # relayer //Ferdie is rewarded for delivering messages from Rococo BH -bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw,0x00000002,0x6268726F,ThisChain,0" within 300 seconds +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with '{ "relayerAccountAddress": "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", "rewardKind": { "RococoWestend": { "laneId": "0x00000002", "bridgedChainId": "0x6268726F", "owner": "ThisChain" }}, "expectedRelayerReward": 1}' within 300 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl index eacac98982ab9..788cbd4fc1bd6 100644 --- a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl @@ -9,4 +9,4 @@ asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "auto-log reserve-tr asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with '{ "accountAddress": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "expectedAssetBalance": 4800000000000, "expectedAssetId": { "parents": 2, "interior": { "X1": [{ "GlobalConsensus": { "ByGenesis": [225,67,242,56,3,172,80,232,246,248,230,38,149,209,206,158,78,29,104,170,54,193,205,44,253,21,52,2,19,243,66,62] } }] }}}' within 600 seconds # relayer //Eve is rewarded for delivering messages from Westend BH -bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL,0x00000002,0x62687764,ThisChain,0" within 300 seconds +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with '{ "relayerAccountAddress": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", "rewardKind": { "laneId": "0x00000002", "bridgedChainId": "0x62687764", "owner": "ThisChain" }, "expectedRelayerReward": 1}' within 300 seconds diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 3995f520caf04..e2b10c711f6c2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -23,6 +23,7 @@ use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; use bp_parachains::SingleParaStoredHeaderDataBuilder; +use bp_relayers::RewardsAccountParams; use frame_support::{parameter_types, traits::ConstU32}; parameter_types! { @@ -68,10 +69,12 @@ pub type RelayersForLegacyLaneIdsMessagesInstance = (); impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; + type RewardKind = RewardsAccountParams; type PaymentProcedure = bp_relayers::PayRewardFromAccount< pallet_balances::Pallet, AccountId, - Self::LaneId, + bp_messages::LegacyLaneId, + Self::Reward, >; type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< AccountId, @@ -81,8 +84,8 @@ impl pallet_bridge_relayers::Config fo RequiredStakeForStakeAndSlash, RelayerStakeLease, >; - type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; - type LaneId = bp_messages::LegacyLaneId; + type Balance = Balance; + type WeightInfo = weights::pallet_bridge_relayers_legacy::WeightInfo; } /// Allows collect and claim rewards for relayers @@ -90,10 +93,12 @@ pub type RelayersForPermissionlessLanesInstance = pallet_bridge_relayers::Instan impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; + type RewardKind = RewardsAccountParams; type PaymentProcedure = bp_relayers::PayRewardFromAccount< pallet_balances::Pallet, AccountId, - Self::LaneId, + bp_messages::HashedLaneId, + Self::Reward, >; type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< AccountId, @@ -103,8 +108,8 @@ impl pallet_bridge_relayers::Config for RequiredStakeForStakeAndSlash, RelayerStakeLease, >; - type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; - type LaneId = bp_messages::HashedLaneId; + type Balance = Balance; + type WeightInfo = weights::pallet_bridge_relayers_permissionless_lanes::WeightInfo; } /// Add GRANDPA bridge pallet to track Rococo Bulletin chain. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 78e83451835f7..1a973193b9625 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -103,7 +103,6 @@ pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersTransacti RelayersForPermissionlessLanesInstance, PriorityBoostPerMessage, >, - LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index b70f8e076057c..8eb7e6719bad3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -95,7 +95,6 @@ pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersTransac RelayersForLegacyLaneIdsMessagesInstance, PriorityBoostPerMessage, >, - LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index fdba23a2915f4..8d38d017c834b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -181,7 +181,11 @@ pub type Migrations = ( OutboundLanesCongestedSignalsKey, RocksDbWeight, >, - pallet_bridge_relayers::migration::v1::MigrationToV1, + pallet_bridge_relayers::migration::v1::MigrationToV1< + Runtime, + bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance, + bp_messages::LegacyLaneId, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, cumulus_pallet_aura_ext::migration::MigrateV0ToV1, @@ -1344,7 +1348,7 @@ impl_runtime_apis! { let bench_lane_id = >::bench_lane_id(); use bp_runtime::Chain; let bridged_chain_id =>::BridgedChain::ID; - pallet_bridge_relayers::Pallet::::relayer_reward( + pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( bench_lane_id, @@ -1470,16 +1474,27 @@ impl_runtime_apis! { } impl BridgeRelayersConfig for Runtime { + fn bench_reward_kind() -> Self::RewardKind { + bp_relayers::RewardsAccountParams::new( + bp_messages::LegacyLaneId::default(), + *b"test", + bp_relayers::RewardsAccountOwner::ThisChain + ) + } + fn prepare_rewards_account( - account_params: bp_relayers::RewardsAccountParams<>::LaneId>, + reward_kind: Self::RewardKind, reward: Balance, - ) { + ) -> Option { let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId, - >::LaneId, - >::rewards_account(account_params); + bp_messages::LegacyLaneId, + Balance, + >::rewards_account(reward_kind); >::deposit_account(rewards_account, reward); + + None } fn deposit_account(account: AccountId, balance: Balance) { @@ -1489,16 +1504,27 @@ impl_runtime_apis! { } impl BridgeRelayersConfig for Runtime { + fn bench_reward_kind() -> Self::RewardKind { + bp_relayers::RewardsAccountParams::new( + bp_messages::HashedLaneId::default(), + *b"test", + bp_relayers::RewardsAccountOwner::ThisChain + ) + } + fn prepare_rewards_account( - account_params: bp_relayers::RewardsAccountParams<>::LaneId>, + reward_kind: Self::RewardKind, reward: Balance, - ) { + ) -> Option { let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId, - >::LaneId, - >::rewards_account(account_params); + bp_messages::HashedLaneId, + Balance, + >::rewards_account(reward_kind); >::deposit_account(rewards_account, reward); + + None } fn deposit_account(account: AccountId, balance: Balance) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 7a0accf2e7a45..ebd7f5640e03b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -34,7 +34,8 @@ pub mod pallet_bridge_grandpa; pub mod pallet_bridge_messages_rococo_to_rococo_bulletin; pub mod pallet_bridge_messages_rococo_to_westend; pub mod pallet_bridge_parachains; -pub mod pallet_bridge_relayers; +pub mod pallet_bridge_relayers_legacy; +pub mod pallet_bridge_relayers_permissionless_lanes; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; @@ -67,7 +68,7 @@ impl GrandpaWeightInfoExt for pallet_bridge_grandpa::WeightInfo // 1) checks whether relayer registration is active from validate/pre_dispatch; // 2) may slash and deregister relayer from post_dispatch // (2) includes (1), so (2) is the worst case - pallet_bridge_relayers::WeightInfo::::slash_and_deregister() + pallet_bridge_relayers_legacy::WeightInfo::::slash_and_deregister() } } @@ -79,12 +80,12 @@ impl MessagesWeightInfoExt } fn receive_messages_proof_overhead_from_runtime() -> Weight { - pallet_bridge_relayers::WeightInfo::::receive_messages_proof_overhead_from_runtime( + pallet_bridge_relayers_permissionless_lanes::WeightInfo::::receive_messages_proof_overhead_from_runtime( ) } fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - pallet_bridge_relayers::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() + pallet_bridge_relayers_permissionless_lanes::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() } } @@ -96,12 +97,12 @@ impl MessagesWeightInfoExt } fn receive_messages_proof_overhead_from_runtime() -> Weight { - pallet_bridge_relayers::WeightInfo::::receive_messages_proof_overhead_from_runtime( + pallet_bridge_relayers_legacy::WeightInfo::::receive_messages_proof_overhead_from_runtime( ) } fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - pallet_bridge_relayers::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() + pallet_bridge_relayers_legacy::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() } } @@ -115,6 +116,6 @@ impl ParachainsWeightInfoExt for pallet_bridge_parachains::WeightInfo::slash_and_deregister() + pallet_bridge_relayers_legacy::WeightInfo::::slash_and_deregister() } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_legacy.rs similarity index 71% rename from cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs rename to cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_legacy.rs index c30abd093d14b..25a920fa21f77 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_legacy.rs @@ -1,5 +1,4 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,25 +16,28 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-02-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `a763a8995546`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 // Executed Command: -// target/production/polkadot-parachain +// frame-omni-bencher +// v1 // benchmark // pallet -// --steps=50 -// --repeat=20 // --extrinsic=* +// --runtime=target/production/wbuild/bridge-hub-rococo-runtime/bridge_hub_rococo_runtime.wasm +// --pallet=pallet_bridge_relayers +// --header=/__w/polkadot-sdk/polkadot-sdk/cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights // --wasm-execution=compiled +// --steps=50 +// --repeat=20 // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_bridge_relayers -// --chain=bridge-hub-rococo-dev -// --header=./cumulus/file_header.txt -// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --no-storage-info +// --no-min-squares +// --no-median-slopes #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,19 +51,29 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `306` + // Measured: `278` // Estimated: `3593` - // Minimum execution time: 53_924_000 picoseconds. - Weight::from_parts(54_736_000, 0) + // Minimum execution time: 50_375_000 picoseconds. + Weight::from_parts(51_947_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_rewards_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `BridgeRelayers::RegisteredRelayers` (r:1 w:1) /// Proof: `BridgeRelayers::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) @@ -72,8 +84,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `131` // Estimated: `4714` - // Minimum execution time: 28_608_000 picoseconds. - Weight::from_parts(29_081_000, 0) + // Minimum execution time: 27_761_000 picoseconds. + Weight::from_parts(29_163_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +98,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `231` // Estimated: `4714` - // Minimum execution time: 29_738_000 picoseconds. - Weight::from_parts(30_242_000, 0) + // Minimum execution time: 32_981_000 picoseconds. + Weight::from_parts(34_143_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -96,27 +108,25 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `BridgeRelayers::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Reserves` (r:1 w:1) /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn slash_and_deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `334` + // Measured: `231` // Estimated: `4714` - // Minimum execution time: 33_174_000 picoseconds. - Weight::from_parts(33_992_000, 0) + // Minimum execution time: 27_188_000 picoseconds. + Weight::from_parts(28_098_000, 0) .saturating_add(Weight::from_parts(0, 4714)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `3567` - // Minimum execution time: 7_950_000 picoseconds. - Weight::from_parts(8_123_000, 0) - .saturating_add(Weight::from_parts(0, 3567)) + // Estimated: `3538` + // Minimum execution time: 7_269_000 picoseconds. + Weight::from_parts(7_639_000, 0) + .saturating_add(Weight::from_parts(0, 3538)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_permissionless_lanes.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_permissionless_lanes.rs new file mode 100644 index 0000000000000..79918088f33c5 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers_permissionless_lanes.rs @@ -0,0 +1,133 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_bridge_relayers` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-02-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `a763a8995546`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 + +// Executed Command: +// frame-omni-bencher +// v1 +// benchmark +// pallet +// --extrinsic=* +// --runtime=target/production/wbuild/bridge-hub-rococo-runtime/bridge_hub_rococo_runtime.wasm +// --pallet=pallet_bridge_relayers +// --header=/__w/polkadot-sdk/polkadot-sdk/cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --wasm-execution=compiled +// --steps=50 +// --repeat=20 +// --heap-pages=4096 +// --no-storage-info +// --no-min-squares +// --no-median-slopes + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_relayers`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_relayers::WeightInfo for WeightInfo { + /// Storage: `BridgeRelayersForPermissionlessLanes::RelayerRewards` (r:1 w:1) + /// Proof: `BridgeRelayersForPermissionlessLanes::RelayerRewards` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `270` + // Estimated: `3593` + // Minimum execution time: 51_845_000 picoseconds. + Weight::from_parts(53_071_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_rewards_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (r:1 w:1) + /// Proof: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) + /// Storage: `Balances::Reserves` (r:1 w:1) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `58` + // Estimated: `4714` + // Minimum execution time: 26_683_000 picoseconds. + Weight::from_parts(27_836_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (r:1 w:1) + /// Proof: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Reserves` (r:1 w:1) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) + fn deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `158` + // Estimated: `4714` + // Minimum execution time: 28_456_000 picoseconds. + Weight::from_parts(28_997_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (r:1 w:1) + /// Proof: `BridgeRelayersForPermissionlessLanes::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Reserves` (r:1 w:1) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) + fn slash_and_deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `158` + // Estimated: `4714` + // Minimum execution time: 22_474_000 picoseconds. + Weight::from_parts(23_057_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `BridgeRelayersForPermissionlessLanes::RelayerRewards` (r:1 w:1) + /// Proof: `BridgeRelayersForPermissionlessLanes::RelayerRewards` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`) + fn register_relayer_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3566` + // Minimum execution time: 7_245_000 picoseconds. + Weight::from_parts(7_816_000, 0) + .saturating_add(Weight::from_parts(0, 3566)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index a5ab4e5137853..40e3fc526497d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -23,26 +23,75 @@ use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; use bp_messages::LegacyLaneId; +use bp_relayers::RewardsAccountParams; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::parameter_types; +use scale_info::TypeInfo; +use xcm::VersionedLocation; parameter_types! { pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; pub const RelayerStakeLease: u32 = 8; pub const RelayerStakeReserveId: [u8; 8] = *b"brdgrlrs"; +} + +/// Showcasing that we can handle multiple different rewards with the same pallet. +#[derive(Clone, Copy, Debug, Decode, Encode, Eq, MaxEncodedLen, PartialEq, TypeInfo)] +pub enum BridgeRewardKind { + /// Rewards for the R/W bridge—distinguished by the `RewardsAccountParams` key. + RococoWestend(RewardsAccountParams), + /// Rewards for Snowbridge. + Snowbridge, +} + +impl From> for BridgeRewardKind { + fn from(value: RewardsAccountParams) -> Self { + Self::RococoWestend(value) + } +} - pub storage DeliveryRewardInBalance: u64 = 1_000_000; +/// Implementation of `bp_relayers::PaymentProcedure` as a pay/claim rewards scheme. +pub struct BridgeRewardPayer; +impl bp_relayers::PaymentProcedure for BridgeRewardPayer { + type Error = sp_runtime::DispatchError; + type AlternativeBeneficiary = VersionedLocation; + + fn pay_reward( + relayer: &AccountId, + reward_kind: BridgeRewardKind, + reward: u128, + alternative_beneficiary: Option, + ) -> Result<(), Self::Error> { + match reward_kind { + BridgeRewardKind::RococoWestend(lane_params) => { + frame_support::ensure!( + alternative_beneficiary.is_none(), + Self::Error::Other("`alternative_beneficiary` is not supported for `RococoWestend` rewards!") + ); + bp_relayers::PayRewardFromAccount::< + Balances, + AccountId, + LegacyLaneId, + u128, + >::pay_reward( + relayer, lane_params, reward, None, + ) + }, + BridgeRewardKind::Snowbridge => + Err(sp_runtime::DispatchError::Other("Not implemented yet, check also `fn prepare_rewards_account` to return `alternative_beneficiary`!")), + } + } } /// Allows collect and claim rewards for relayers -pub type RelayersForLegacyLaneIdsMessagesInstance = (); -impl pallet_bridge_relayers::Config for Runtime { +pub type BridgeRelayersInstance = (); +impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Reward = Balance; - type PaymentProcedure = bp_relayers::PayRewardFromAccount< - pallet_balances::Pallet, - AccountId, - Self::LaneId, - >; + + type Reward = u128; + type RewardKind = BridgeRewardKind; + type PaymentProcedure = BridgeRewardPayer; + type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< AccountId, BlockNumber, @@ -51,6 +100,6 @@ impl pallet_bridge_relayers::Config fo RequiredStakeForStakeAndSlash, RelayerStakeLease, >; + type Balance = Balance; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; - type LaneId = LegacyLaneId; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index cdad13212676a..de5b11d78bcce 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -17,9 +17,7 @@ //! Bridge definitions used on BridgeHub with the Westend flavor. use crate::{ - bridge_common_config::{DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance}, - weights, - xcm_config::UniversalLocation, + bridge_common_config::BridgeRelayersInstance, weights, xcm_config::UniversalLocation, AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, XcmpQueue, }; @@ -78,6 +76,7 @@ parameter_types! { ); pub storage BridgeDeposit: Balance = 10 * WND; + pub storage DeliveryRewardInBalance: u64 = 1_000_000; } /// Proof of messages, coming from Rococo. @@ -99,10 +98,9 @@ pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersTransac StrOnBridgeHubWestendRefundBridgeHubRococoMessages, Runtime, WithBridgeHubRococoMessagesInstance, - RelayersForLegacyLaneIdsMessagesInstance, + BridgeRelayersInstance, PriorityBoostPerMessage, >, - LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages); @@ -152,7 +150,7 @@ impl pallet_bridge_messages::Config for Run type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, WithBridgeHubRococoMessagesInstance, - RelayersForLegacyLaneIdsMessagesInstance, + BridgeRelayersInstance, DeliveryRewardInBalance, >; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 45d08ba8546cd..a459b11cd68b4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -162,7 +162,16 @@ pub type Migrations = ( OutboundLanesCongestedSignalsKey, RocksDbWeight, >, - pallet_bridge_relayers::migration::v1::MigrationToV1, + pallet_bridge_relayers::migration::v1::MigrationToV1< + Runtime, + bridge_common_config::BridgeRelayersInstance, + bp_messages::LegacyLaneId, + >, + pallet_bridge_relayers::migration::v2::MigrationToV2< + Runtime, + bridge_common_config::BridgeRelayersInstance, + bp_messages::LegacyLaneId, + >, snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< Runtime, ConstU32, @@ -1229,12 +1238,14 @@ impl_runtime_apis! { let bench_lane_id = >::bench_lane_id(); use bp_runtime::Chain; let bridged_chain_id =>::BridgedChain::ID; - pallet_bridge_relayers::Pallet::::relayer_reward( + pallet_bridge_relayers::Pallet::::relayer_reward( relayer, - bp_relayers::RewardsAccountParams::new( - bench_lane_id, - bridged_chain_id, - bp_relayers::RewardsAccountOwner::BridgedChain + bridge_common_config::BridgeRewardKind::RococoWestend( + bp_relayers::RewardsAccountParams::new( + bench_lane_id, + bridged_chain_id, + bp_relayers::RewardsAccountOwner::BridgedChain + ) ) ).is_some() } @@ -1309,17 +1320,31 @@ impl_runtime_apis! { } } - impl BridgeRelayersConfig for Runtime { + impl BridgeRelayersConfig for Runtime { + fn bench_reward_kind() -> Self::RewardKind { + bp_relayers::RewardsAccountParams::new( + bp_messages::LegacyLaneId::default(), + *b"test", + bp_relayers::RewardsAccountOwner::ThisChain + ).into() + } + fn prepare_rewards_account( - account_params: bp_relayers::RewardsAccountParams<>::LaneId>, + reward_kind: Self::RewardKind, reward: Balance, - ) { + ) -> Option> { + let bridge_common_config::BridgeRewardKind::RococoWestend(reward_kind) = reward_kind else { + panic!("Unexpected reward_kind: {:?} - not compatible with `bench_reward_kind`!", reward_kind); + }; let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId, - >::LaneId, - >::rewards_account(account_params); + bp_messages::LegacyLaneId, + u128, + >::rewards_account(reward_kind); Self::deposit_account(rewards_account, reward); + + None } fn deposit_account(account: AccountId, balance: Balance) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs index 4127624f1b87c..4d8381bb84e2d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs @@ -1,5 +1,4 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,25 +16,28 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-02-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 +//! HOSTNAME: `b50e56a3e1c7`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 // Executed Command: -// target/production/polkadot-parachain +// frame-omni-bencher +// v1 // benchmark // pallet -// --steps=50 -// --repeat=20 // --extrinsic=* +// --runtime=target/production/wbuild/bridge-hub-westend-runtime/bridge_hub_westend_runtime.wasm +// --pallet=pallet_bridge_relayers +// --header=/__w/polkadot-sdk/polkadot-sdk/cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights // --wasm-execution=compiled +// --steps=50 +// --repeat=20 // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_bridge_relayers -// --chain=bridge-hub-westend-dev -// --header=./cumulus/file_header.txt -// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ +// --no-storage-info +// --no-min-squares +// --no-median-slopes #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,19 +51,29 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `272` + // Measured: `245` // Estimated: `3593` - // Minimum execution time: 52_499_000 picoseconds. - Weight::from_parts(53_659_000, 0) + // Minimum execution time: 51_920_000 picoseconds. + Weight::from_parts(53_320_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_rewards_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `BridgeRelayers::RegisteredRelayers` (r:1 w:1) /// Proof: `BridgeRelayers::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) @@ -72,8 +84,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `97` // Estimated: `4714` - // Minimum execution time: 28_706_000 picoseconds. - Weight::from_parts(29_434_000, 0) + // Minimum execution time: 28_380_000 picoseconds. + Weight::from_parts(29_192_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +98,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `197` // Estimated: `4714` - // Minimum execution time: 29_563_000 picoseconds. - Weight::from_parts(30_222_000, 0) + // Minimum execution time: 29_084_000 picoseconds. + Weight::from_parts(30_297_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -96,27 +108,25 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `BridgeRelayers::RegisteredRelayers` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Reserves` (r:1 w:1) /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn slash_and_deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `300` + // Measured: `197` // Estimated: `4714` - // Minimum execution time: 32_618_000 picoseconds. - Weight::from_parts(33_528_000, 0) + // Minimum execution time: 23_805_000 picoseconds. + Weight::from_parts(24_552_000, 0) .saturating_add(Weight::from_parts(0, 4714)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: // Measured: `42` - // Estimated: `3567` - // Minimum execution time: 7_521_000 picoseconds. - Weight::from_parts(7_844_000, 0) - .saturating_add(Weight::from_parts(0, 3567)) + // Estimated: `3539` + // Minimum execution time: 7_220_000 picoseconds. + Weight::from_parts(7_497_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index c9a97af634615..291f45091b2c0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -18,29 +18,41 @@ use bp_messages::LegacyLaneId; use bp_polkadot_core::Signature; +use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; use bridge_common_config::{ - DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance, - RequiredStakeForStakeAndSlash, + BridgeRelayersInstance, BridgeRewardKind, RequiredStakeForStakeAndSlash, +}; +use bridge_hub_test_utils::{ + test_cases::{from_parachain, run_test}, + SlotDurations, }; -use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations}; use bridge_hub_westend_runtime::{ bridge_common_config, bridge_to_rococo_config, bridge_to_rococo_config::RococoGlobalConsensusNetwork, xcm_config::{LocationToAccountId, RelayNetwork, WestendLocation, XcmConfig}, - AllPalletsWithoutSystem, Block, BridgeRejectObsoleteHeadersAndMessages, Executive, - ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, - RuntimeOrigin, SessionKeys, TransactionPayment, TxExtension, UncheckedExtrinsic, + AllPalletsWithoutSystem, Balances, Block, BridgeRejectObsoleteHeadersAndMessages, + BridgeRelayers, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, TransactionPayment, TxExtension, + UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, - WithBridgeHubRococoMessagesInstance, XcmOverBridgeHubRococoInstance, + DeliveryRewardInBalance, WithBridgeHubRococoMessagesInstance, XcmOverBridgeHubRococoInstance, }; use codec::{Decode, Encode}; -use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; +use frame_support::{ + assert_err, assert_ok, + dispatch::GetDispatchInfo, + parameter_types, + traits::{ + fungible::{Inspect, Mutate}, + ConstU8, + }, +}; use parachains_common::{AccountId, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_core::crypto::Ss58Codec; -use sp_keyring::Sr25519Keyring::Alice; +use sp_keyring::Sr25519Keyring::{Alice, Bob}; use sp_runtime::{ generic::{Era, SignedPayload}, AccountId32, Perbill, @@ -69,7 +81,7 @@ type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< BridgeGrandpaRococoInstance, BridgeParachainRococoInstance, WithBridgeHubRococoMessagesInstance, - RelayersForLegacyLaneIdsMessagesInstance, + BridgeRelayersInstance, >; parameter_types! { @@ -534,3 +546,90 @@ fn xcm_payment_api_works() { Block, >(); } + +#[test] +pub fn bridge_rewards_works() { + run_test::( + collator_session_keys(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + vec![], + || { + // reward in WNDs + let reward1: u128 = 2_000_000_000; + // reward in WETH + let reward2: u128 = 3_000_000_000; + + // prepare accounts + let account1 = AccountId32::from(Alice); + let account2 = AccountId32::from(Bob); + let reward1_for = RewardsAccountParams::new( + LegacyLaneId([1; 4]), + *b"test", + RewardsAccountOwner::ThisChain, + ); + let expected_reward1_account = + PayRewardFromAccount::<(), AccountId, LegacyLaneId, ()>::rewards_account( + reward1_for, + ); + assert_ok!(Balances::mint_into(&expected_reward1_account, ExistentialDeposit::get())); + assert_ok!(Balances::mint_into(&expected_reward1_account, reward1.into())); + assert_ok!(Balances::mint_into(&account1, ExistentialDeposit::get())); + + // register rewards + use bp_relayers::RewardLedger; + BridgeRelayers::register_reward( + &account1, + BridgeRewardKind::from(reward1_for), + reward1, + ); + BridgeRelayers::register_reward(&account2, BridgeRewardKind::Snowbridge, reward2); + + // check stored rewards + assert_eq!( + BridgeRelayers::relayer_reward(&account1, BridgeRewardKind::from(reward1_for)), + Some(reward1) + ); + assert_eq!( + BridgeRelayers::relayer_reward(&account1, BridgeRewardKind::Snowbridge), + None, + ); + assert_eq!( + BridgeRelayers::relayer_reward(&account2, BridgeRewardKind::Snowbridge), + Some(reward2), + ); + assert_eq!( + BridgeRelayers::relayer_reward(&account2, BridgeRewardKind::from(reward1_for)), + None, + ); + + // claim rewards + assert_ok!(BridgeRelayers::claim_rewards( + RuntimeOrigin::signed(account1.clone()), + reward1_for.into() + )); + assert_eq!(Balances::total_balance(&account1), ExistentialDeposit::get() + reward1); + assert_eq!( + BridgeRelayers::relayer_reward(&account1, BridgeRewardKind::from(reward1_for)), + None, + ); + + // already claimed + assert_err!( + BridgeRelayers::claim_rewards( + RuntimeOrigin::signed(account1.clone()), + reward1_for.into() + ), + pallet_bridge_relayers::Error::::NoRewardForRelayer + ); + + // not yet implemented for Snowbridge + assert_err!( + BridgeRelayers::claim_rewards( + RuntimeOrigin::signed(account2.clone()), + BridgeRewardKind::Snowbridge + ), + pallet_bridge_relayers::Error::::FailedToPayReward + ); + }, + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index 036223759e655..f273312bc3e77 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs @@ -50,7 +50,7 @@ pub trait WithRemoteGrandpaChainHelper { Self::MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config>; + > + pallet_bridge_relayers::Config; /// All pallets of this chain, excluding system pallet. type AllPalletsWithoutSystem: OnInitialize> + OnFinalize>; @@ -61,6 +61,8 @@ pub trait WithRemoteGrandpaChainHelper { /// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI` /// instance. type RPI: 'static; + /// Relayer reward type. + type RelayerRewardKind: From>>; } /// Adapter struct that implements [`WithRemoteGrandpaChainHelper`]. @@ -78,9 +80,11 @@ where MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config>, + > + pallet_bridge_relayers::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, + >::RewardKind: + From>>, GPI: 'static, MPI: 'static, RPI: 'static, @@ -90,6 +94,7 @@ where type GPI = GPI; type MPI = MPI; type RPI = RPI; + type RelayerRewardKind = Runtime::RewardKind; } /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index 4fbfe18b9953f..d223951fc41cc 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs @@ -53,7 +53,7 @@ pub trait WithRemoteParachainHelper { Self::MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config>; + > + pallet_bridge_relayers::Config; /// All pallets of this chain, excluding system pallet. type AllPalletsWithoutSystem: OnInitialize> + OnFinalize>; @@ -66,6 +66,8 @@ pub trait WithRemoteParachainHelper { /// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI` /// instance. type RPI: 'static; + /// Relayer reward type. + type RelayerRewardKind: From>>; } /// Adapter struct that implements `WithRemoteParachainHelper`. @@ -84,9 +86,11 @@ where MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config>, + > + pallet_bridge_relayers::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, + >::RewardKind: + From>>, GPI: 'static, PPI: 'static, MPI: 'static, @@ -98,6 +102,7 @@ where type PPI = PPI; type MPI = MPI; type RPI = RPI; + type RelayerRewardKind = Runtime::RewardKind; } /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index 1d6935b843b74..79c51f4dbd84b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -21,7 +21,6 @@ use crate::test_cases::{bridges_prelude::*, run_test, RuntimeHelper}; use asset_test_utils::BasicParachainRuntime; use bp_messages::MessageNonce; use bp_polkadot_core::parachains::{ParaHash, ParaId}; -use bp_relayers::RewardsAccountParams; use bp_runtime::Chain; use codec::Decode; use core::marker::PhantomData; @@ -168,7 +167,7 @@ where /// Verifies that relayer is rewarded at this chain. pub struct VerifyRelayerRewarded, RPI: 'static> { relayer: Runtime::AccountId, - reward_params: RewardsAccountParams, + reward_params: Runtime::RewardKind, } impl VerifyRelayerRewarded @@ -179,9 +178,9 @@ where /// Expect given delivered nonce to be the latest after transaction. pub fn expect_relayer_reward( relayer: Runtime::AccountId, - reward_params: RewardsAccountParams, + reward_params: impl Into, ) -> Box { - Box::new(Self { relayer, reward_params }) + Box::new(Self { relayer, reward_params: reward_params.into() }) } } diff --git a/prdoc/pr_7492.prdoc b/prdoc/pr_7492.prdoc new file mode 100644 index 0000000000000..310b46a54b3dc --- /dev/null +++ b/prdoc/pr_7492.prdoc @@ -0,0 +1,20 @@ +title: Make `pallet-bridge-rewards` generic over `RewardKind` +doc: +- audience: Runtime Dev + description: |- + The PR enhances the pallet-bridge-rewards by making it generic over the RewardKind type (previously hardcoded as RewardsAccountParams). This modification allows the pallet to support multiple reward types (e.g., P/K bridge, Snowbridge), increasing its flexibility and applicability across various bridge scenarios. + + Other pallets can register rewards using bp_relayers::RewardLedger, which is implemented by the rewards pallet. The runtime can then be configured with different mechanisms for paying/claiming rewards via bp_relayers::PaymentProcedure (e.g., see the pub struct BridgeRewardPayer; implementation for BridgeHubWestend). +crates: +- name: bridge-runtime-common + bump: patch +- name: pallet-bridge-relayers + bump: major +- name: bp-relayers + bump: major +- name: bridge-hub-rococo-runtime + bump: minor +- name: bridge-hub-westend-runtime + bump: major +- name: bridge-hub-test-utils + bump: major