referrerpolicy=no-referrer-when-downgrade

pallet_transaction_payment/
benchmarking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Benchmarks for Transaction Payment Pallet's transaction extension
19
20extern crate alloc;
21
22use super::*;
23use crate::Pallet;
24use frame_benchmarking::v2::*;
25use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
26use frame_system::{EventRecord, RawOrigin};
27use sp_runtime::traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable};
28
29/// Benchmark configuration trait.
30///
31/// This extends the pallet's Config trait to allow runtimes to set up any
32/// required state before running benchmarks. For example, runtimes that
33/// distribute fees to block authors may need to set the author before
34/// the benchmark runs.
35pub trait Config: crate::Config {
36	/// Called at the start of each benchmark to set up any required state.
37	///
38	/// The default implementation is a no-op. Runtimes can override this
39	/// to perform setup like setting the block author for fee distribution.
40	fn setup_benchmark_environment() {}
41}
42
43fn assert_last_event<T: crate::Config>(generic_event: <T as crate::Config>::RuntimeEvent) {
44	let events = frame_system::Pallet::<T>::events();
45	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
46	// compare to the last event record
47	let EventRecord { event, .. } = &events[events.len() - 1];
48	assert_eq!(event, &system_event);
49}
50
51#[benchmarks(where
52	T: Config,
53	T::RuntimeOrigin: AsTransactionAuthorizedOrigin,
54	T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
55)]
56mod benchmarks {
57	use super::*;
58
59	#[benchmark]
60	fn charge_transaction_payment() {
61		<T as Config>::setup_benchmark_environment();
62
63		let caller: T::AccountId = account("caller", 0, 0);
64		let existential_deposit =
65			<T::OnChargeTransaction as OnChargeTransaction<T>>::minimum_balance();
66
67		// Use a reasonable minimum tip that works for most runtimes
68		let min_tip: BalanceOf<T> = 1_000_000_000u32.into();
69		let tip = if existential_deposit.is_zero() { min_tip } else { existential_deposit };
70
71		// Build the call and dispatch info first so we can compute the actual fee
72		let ext: ChargeTransactionPayment<T> = ChargeTransactionPayment::from(tip);
73		let inner = frame_system::Call::remark { remark: alloc::vec![] };
74		let call = T::RuntimeCall::from(inner);
75		let extension_weight = ext.weight(&call);
76		let info = DispatchInfo {
77			call_weight: Weight::from_parts(100, 0),
78			extension_weight,
79			class: DispatchClass::Operational,
80			pays_fee: Pays::Yes,
81		};
82		let mut post_info = PostDispatchInfo {
83			actual_weight: Some(Weight::from_parts(10, 0)),
84			pays_fee: Pays::Yes,
85		};
86
87		// Calculate the actual fee that will be charged, then endow enough to cover it
88		// with a 10x buffer to account for any fee multiplier variations.
89		// Ensure we endow at least the existential deposit so the account can exist.
90		let len: u32 = 10;
91		let expected_fee = Pallet::<T>::compute_fee(len, &info, tip);
92		let amount_to_endow = expected_fee.max(existential_deposit).saturating_mul(10u32.into());
93
94		<T::OnChargeTransaction as OnChargeTransaction<T>>::endow_account(&caller, amount_to_endow);
95
96		#[block]
97		{
98			assert!(ext
99				.test_run(
100					RawOrigin::Signed(caller.clone()).into(),
101					&call,
102					&info,
103					len as usize,
104					0,
105					|_| Ok(post_info)
106				)
107				.unwrap()
108				.is_ok());
109		}
110
111		post_info.actual_weight.as_mut().map(|w| w.saturating_accrue(extension_weight));
112		let actual_fee = Pallet::<T>::compute_actual_fee(len, &info, &post_info, tip);
113		assert_last_event::<T>(
114			Event::<T>::TransactionFeePaid { who: caller, actual_fee, tip }.into(),
115		);
116	}
117
118	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime);
119}