referrerpolicy=no-referrer-when-downgrade

pallet_bridge_relayers/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Benchmarks for the relayers Pallet.
18
19#![cfg(feature = "runtime-benchmarks")]
20
21use crate::*;
22
23use frame_benchmarking::v2::*;
24use frame_support::{assert_ok, weights::Weight};
25use frame_system::RawOrigin;
26use sp_runtime::traits::One;
27
28/// Reward amount that is (hopefully) is larger than existential deposit across all chains.
29const REWARD_AMOUNT: u32 = u32::MAX;
30
31/// Pallet we're benchmarking here.
32pub struct Pallet<T: Config<I>, I: 'static = ()>(crate::Pallet<T, I>);
33
34/// Trait that must be implemented by runtime.
35pub trait Config<I: 'static = ()>: crate::Config<I> {
36	/// `T::Reward` to use in benchmarks.
37	fn bench_reward() -> Self::Reward;
38	/// Prepare environment for paying the given reward, and optionally return the
39	/// `(reward_kind, beneficiary)` pair to use for the `claim_rewards_to`
40	/// benchmark. Returning `Some` enables that benchmark and lets the runtime
41	/// pick a different reward kind than `bench_reward()` for it (e.g., a
42	/// Snowbridge reward routed via XCM to an `AssetHubLocation`, which is not
43	/// valid for the basic `claim_rewards` extrinsic). Implementations should
44	/// also fund `relayer` with whatever balance the payment path needs (e.g.,
45	/// XCM delivery fees).
46	///
47	/// Returning `None` causes `claim_rewards_to` to be assigned `Weight::MAX`.
48	fn prepare_rewards_account(
49		relayer: &Self::AccountId,
50		reward_kind: Self::Reward,
51		reward: Self::RewardBalance,
52	) -> Option<(Self::Reward, BeneficiaryOf<Self, I>)>;
53	/// Give enough balance to given account.
54	fn deposit_account(account: Self::AccountId, balance: Self::Balance);
55}
56
57fn assert_last_event<T: Config<I>, I: 'static>(
58	generic_event: <T as pallet::Config<I>>::RuntimeEvent,
59) {
60	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
61}
62
63#[instance_benchmarks(
64	where
65		BeneficiaryOf<T, I>: From<<T as frame_system::Config>::AccountId>,
66)]
67mod benchmarks {
68	use super::*;
69
70	#[benchmark]
71	fn claim_rewards() {
72		let relayer: T::AccountId = whitelisted_caller();
73		let reward_kind = T::bench_reward();
74		let reward_balance = T::RewardBalance::from(REWARD_AMOUNT);
75		let _ = T::prepare_rewards_account(&relayer, reward_kind, reward_balance);
76		RelayerRewards::<T, I>::insert(&relayer, reward_kind, reward_balance);
77
78		#[extrinsic_call]
79		_(RawOrigin::Signed(relayer.clone()), reward_kind);
80
81		// we can't check anything here, because `PaymentProcedure` is responsible for
82		// payment logic, so we assume that if call has succeeded, the procedure has
83		// also completed successfully
84		assert_last_event::<T, I>(
85			Event::RewardPaid {
86				relayer: relayer.clone(),
87				reward_kind,
88				reward_balance,
89				beneficiary: relayer.into(),
90			}
91			.into(),
92		);
93	}
94
95	#[benchmark]
96	fn claim_rewards_to() -> Result<(), BenchmarkError> {
97		let relayer: T::AccountId = whitelisted_caller();
98		let reward_balance = T::RewardBalance::from(REWARD_AMOUNT);
99
100		let Some((reward_kind, alternative_beneficiary)) =
101			T::prepare_rewards_account(&relayer, T::bench_reward(), reward_balance)
102		else {
103			return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)));
104		};
105		RelayerRewards::<T, I>::insert(&relayer, reward_kind, reward_balance);
106
107		#[extrinsic_call]
108		_(RawOrigin::Signed(relayer.clone()), reward_kind, alternative_beneficiary.clone());
109
110		// we can't check anything here, because `PaymentProcedure` is responsible for
111		// payment logic, so we assume that if call has succeeded, the procedure has
112		// also completed successfully
113		assert_last_event::<T, I>(
114			Event::RewardPaid {
115				relayer: relayer.clone(),
116				reward_kind,
117				reward_balance,
118				beneficiary: alternative_beneficiary,
119			}
120			.into(),
121		);
122
123		Ok(())
124	}
125
126	#[benchmark]
127	fn register() {
128		let relayer: T::AccountId = whitelisted_caller();
129		let valid_till = frame_system::Pallet::<T>::block_number()
130			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
131			.saturating_add(One::one())
132			.saturating_add(One::one());
133		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
134
135		#[extrinsic_call]
136		_(RawOrigin::Signed(relayer.clone()), valid_till);
137
138		assert!(crate::Pallet::<T, I>::is_registration_active(&relayer));
139	}
140
141	#[benchmark]
142	fn deregister() {
143		let relayer: T::AccountId = whitelisted_caller();
144		let valid_till = frame_system::Pallet::<T>::block_number()
145			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
146			.saturating_add(One::one())
147			.saturating_add(One::one());
148		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
149		crate::Pallet::<T, I>::register(RawOrigin::Signed(relayer.clone()).into(), valid_till)
150			.unwrap();
151		frame_system::Pallet::<T>::set_block_number(valid_till.saturating_add(One::one()));
152
153		#[extrinsic_call]
154		_(RawOrigin::Signed(relayer.clone()));
155
156		assert!(!crate::Pallet::<T, I>::is_registration_active(&relayer));
157	}
158
159	// Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to
160	// the weight of message delivery call if `BridgeRelayersTransactionExtension` signed extension
161	// is deployed at runtime level.
162	#[benchmark]
163	fn slash_and_deregister() {
164		// prepare and register relayer account
165		let relayer: T::AccountId = whitelisted_caller();
166		let valid_till = frame_system::Pallet::<T>::block_number()
167			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
168			.saturating_add(One::one())
169			.saturating_add(One::one());
170		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
171		assert_ok!(crate::Pallet::<T, I>::register(
172			RawOrigin::Signed(relayer.clone()).into(),
173			valid_till
174		));
175
176		// create slash destination account
177		let slash_destination: T::AccountId = whitelisted_caller();
178		T::deposit_account(slash_destination.clone(), Zero::zero());
179
180		#[block]
181		{
182			crate::Pallet::<T, I>::slash_and_deregister(
183				&relayer,
184				bp_relayers::ExplicitOrAccountParams::Explicit::<_, ()>(slash_destination),
185			);
186		}
187
188		assert!(!crate::Pallet::<T, I>::is_registration_active(&relayer));
189	}
190
191	// Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to
192	// the weight of message delivery call if `BridgeRelayersTransactionExtension` signed extension
193	// is deployed at runtime level.
194	#[benchmark]
195	fn register_relayer_reward() {
196		let reward_kind = T::bench_reward();
197		let relayer: T::AccountId = whitelisted_caller();
198
199		#[block]
200		{
201			crate::Pallet::<T, I>::register_relayer_reward(reward_kind, &relayer, One::one());
202		}
203
204		assert_eq!(RelayerRewards::<T, I>::get(relayer, &reward_kind), Some(One::one()));
205	}
206
207	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime);
208}