referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_common/auctions/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot 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// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Benchmarking for auctions pallet
18
19#![cfg(feature = "runtime-benchmarks")]
20use super::{Pallet as Auctions, *};
21use frame_support::{
22	assert_ok,
23	traits::{EnsureOrigin, OnInitialize},
24};
25use frame_system::RawOrigin;
26use polkadot_runtime_parachains::paras;
27use sp_runtime::{traits::Bounded, SaturatedConversion};
28
29use frame_benchmarking::v2::*;
30
31fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
32	let events = frame_system::Pallet::<T>::events();
33	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
34	// compare to the last event record
35	let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
36	assert_eq!(event, &system_event);
37}
38
39fn fill_winners<T: Config + paras::Config>(lease_period_index: LeasePeriodOf<T>) {
40	let auction_index = AuctionCounter::<T>::get();
41	let minimum_balance = CurrencyOf::<T>::minimum_balance();
42
43	for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 {
44		let owner = account("owner", n, 0);
45		let worst_validation_code = T::Registrar::worst_validation_code();
46		let worst_head_data = T::Registrar::worst_head_data();
47		CurrencyOf::<T>::make_free_balance_be(&owner, BalanceOf::<T>::max_value());
48
49		assert!(T::Registrar::register(
50			owner,
51			ParaId::from(n),
52			worst_head_data,
53			worst_validation_code
54		)
55		.is_ok());
56	}
57	assert_ok!(paras::Pallet::<T>::add_trusted_validation_code(
58		frame_system::Origin::<T>::Root.into(),
59		T::Registrar::worst_validation_code(),
60	));
61
62	T::Registrar::execute_pending_transitions();
63
64	for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 {
65		let bidder = account("bidder", n, 0);
66		CurrencyOf::<T>::make_free_balance_be(&bidder, BalanceOf::<T>::max_value());
67
68		let slot_range = SlotRange::n((n - 1) as u8).unwrap();
69		let (start, end) = slot_range.as_pair();
70
71		assert!(Auctions::<T>::bid(
72			RawOrigin::Signed(bidder).into(),
73			ParaId::from(n),
74			auction_index,
75			lease_period_index + start.into(),        // First Slot
76			lease_period_index + end.into(),          // Last slot
77			minimum_balance.saturating_mul(n.into()), // Amount
78		)
79		.is_ok());
80	}
81}
82
83#[benchmarks(
84		where T: pallet_babe::Config + paras::Config,
85	)]
86mod benchmarks {
87	use super::*;
88
89	#[benchmark]
90	fn new_auction() -> Result<(), BenchmarkError> {
91		let duration = BlockNumberFor::<T>::max_value();
92		let lease_period_index = LeasePeriodOf::<T>::max_value();
93		let origin =
94			T::InitiateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
95
96		#[extrinsic_call]
97		_(origin as T::RuntimeOrigin, duration, lease_period_index);
98
99		assert_last_event::<T>(
100			Event::<T>::AuctionStarted {
101				auction_index: AuctionCounter::<T>::get(),
102				lease_period: LeasePeriodOf::<T>::max_value(),
103				ending: BlockNumberFor::<T>::max_value(),
104			}
105			.into(),
106		);
107
108		Ok(())
109	}
110
111	// Worst case scenario a new bid comes in which kicks out an existing bid for the same slot.
112	#[benchmark]
113	fn bid() -> Result<(), BenchmarkError> {
114		// If there is an offset, we need to be on that block to be able to do lease things.
115		let (_, offset) = T::Leaser::lease_period_length();
116		frame_system::Pallet::<T>::set_block_number(offset + One::one());
117
118		// Create a new auction
119		let duration = BlockNumberFor::<T>::max_value();
120		let lease_period_index = LeasePeriodOf::<T>::zero();
121		let origin = T::InitiateOrigin::try_successful_origin()
122			.expect("InitiateOrigin has no successful origin required for the benchmark");
123		Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
124
125		let para = ParaId::from(0);
126		let new_para = ParaId::from(1_u32);
127
128		// Register the paras
129		let owner = account("owner", 0, 0);
130		CurrencyOf::<T>::make_free_balance_be(&owner, BalanceOf::<T>::max_value());
131		let worst_head_data = T::Registrar::worst_head_data();
132		let worst_validation_code = T::Registrar::worst_validation_code();
133		T::Registrar::register(
134			owner.clone(),
135			para,
136			worst_head_data.clone(),
137			worst_validation_code.clone(),
138		)?;
139		T::Registrar::register(owner, new_para, worst_head_data, worst_validation_code.clone())?;
140		assert_ok!(paras::Pallet::<T>::add_trusted_validation_code(
141			frame_system::Origin::<T>::Root.into(),
142			worst_validation_code,
143		));
144
145		T::Registrar::execute_pending_transitions();
146
147		// Make an existing bid
148		let auction_index = AuctionCounter::<T>::get();
149		let first_slot = AuctionInfo::<T>::get().unwrap().0;
150		let last_slot = first_slot + 3u32.into();
151		let first_amount = CurrencyOf::<T>::minimum_balance();
152		let first_bidder: T::AccountId = account("first_bidder", 0, 0);
153		CurrencyOf::<T>::make_free_balance_be(&first_bidder, BalanceOf::<T>::max_value());
154		Auctions::<T>::bid(
155			RawOrigin::Signed(first_bidder.clone()).into(),
156			para,
157			auction_index,
158			first_slot,
159			last_slot,
160			first_amount,
161		)?;
162
163		let caller: T::AccountId = whitelisted_caller();
164		CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
165		let bigger_amount = CurrencyOf::<T>::minimum_balance().saturating_mul(10u32.into());
166		assert_eq!(CurrencyOf::<T>::reserved_balance(&first_bidder), first_amount);
167
168		#[extrinsic_call]
169		_(
170			RawOrigin::Signed(caller.clone()),
171			new_para,
172			auction_index,
173			first_slot,
174			last_slot,
175			bigger_amount,
176		);
177
178		// Confirms that we unreserved funds from a previous bidder, which is worst case
179		// scenario.
180		assert_eq!(CurrencyOf::<T>::reserved_balance(&caller), bigger_amount);
181
182		Ok(())
183	}
184
185	// Worst case: 10 bidders taking all wining spots, and we need to calculate the winner for
186	// auction end. Entire winner map should be full and removed at the end of the benchmark.
187	#[benchmark]
188	fn on_initialize() -> Result<(), BenchmarkError> {
189		// If there is an offset, we need to be on that block to be able to do lease things.
190		let (lease_length, offset) = T::Leaser::lease_period_length();
191		frame_system::Pallet::<T>::set_block_number(offset + One::one());
192
193		// Create a new auction
194		let duration: BlockNumberFor<T> = lease_length / 2u32.into();
195		let lease_period_index = LeasePeriodOf::<T>::zero();
196		let now = frame_system::Pallet::<T>::block_number();
197		let origin = T::InitiateOrigin::try_successful_origin()
198			.expect("InitiateOrigin has no successful origin required for the benchmark");
199		Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
200
201		fill_winners::<T>(lease_period_index);
202
203		for winner in Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap().iter() {
204			assert!(winner.is_some());
205		}
206
207		let winning_data = Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap();
208		// Make winning map full
209		for i in 0u32..(T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() {
210			Winning::<T>::insert(BlockNumberFor::<T>::from(i), winning_data.clone());
211		}
212
213		// Move ahead to the block we want to initialize
214		frame_system::Pallet::<T>::set_block_number(duration + now + T::EndingPeriod::get());
215
216		// Trigger epoch change for new random number value:
217		{
218			pallet_babe::EpochStart::<T>::set((Zero::zero(), u32::MAX.into()));
219			pallet_babe::Pallet::<T>::on_initialize(duration + now + T::EndingPeriod::get());
220			let authorities = pallet_babe::Pallet::<T>::authorities();
221			// Check for non empty authority set since it otherwise emits a No-OP warning.
222			if !authorities.is_empty() {
223				pallet_babe::Pallet::<T>::enact_epoch_change(
224					authorities.clone(),
225					authorities,
226					None,
227				);
228			}
229		}
230
231		#[block]
232		{
233			Auctions::<T>::on_initialize(duration + now + T::EndingPeriod::get());
234		}
235
236		let auction_index = AuctionCounter::<T>::get();
237		assert_last_event::<T>(Event::<T>::AuctionClosed { auction_index }.into());
238		assert!(Winning::<T>::iter().count().is_zero());
239
240		Ok(())
241	}
242
243	// Worst case: 10 bidders taking all wining spots, and winning data is full.
244	#[benchmark]
245	fn cancel_auction() -> Result<(), BenchmarkError> {
246		// If there is an offset, we need to be on that block to be able to do lease things.
247		let (lease_length, offset) = T::Leaser::lease_period_length();
248		frame_system::Pallet::<T>::set_block_number(offset + One::one());
249
250		// Create a new auction
251		let duration: BlockNumberFor<T> = lease_length / 2u32.into();
252		let lease_period_index = LeasePeriodOf::<T>::zero();
253		let origin = T::InitiateOrigin::try_successful_origin()
254			.expect("InitiateOrigin has no successful origin required for the benchmark");
255		Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
256
257		fill_winners::<T>(lease_period_index);
258
259		let winning_data = Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap();
260		for winner in winning_data.iter() {
261			assert!(winner.is_some());
262		}
263
264		// Make winning map full
265		for i in 0u32..(T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() {
266			Winning::<T>::insert(BlockNumberFor::<T>::from(i), winning_data.clone());
267		}
268		assert!(AuctionInfo::<T>::get().is_some());
269
270		#[extrinsic_call]
271		_(RawOrigin::Root);
272
273		assert!(AuctionInfo::<T>::get().is_none());
274		Ok(())
275	}
276
277	impl_benchmark_test_suite!(
278		Auctions,
279		crate::integration_tests::new_test_ext(),
280		crate::integration_tests::Test,
281	);
282}