referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/paras/
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
17use super::*;
18use crate::configuration::HostConfiguration;
19use alloc::vec;
20use frame_benchmarking::v2::*;
21use frame_support::traits::fungible::Mutate;
22use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
23use polkadot_primitives::{
24	HeadData, Id as ParaId, ValidationCode, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE,
25};
26use sp_runtime::traits::{One, Saturating};
27
28pub mod mmr_setup;
29mod pvf_check;
30
31use self::pvf_check::{VoteCause, VoteOutcome};
32
33// 2 ^ 10, because binary search time complexity is O(log(2, n)) and n = 1024 gives us a big and
34// round number.
35// Due to the limited number of parachains, the number of pruning, upcoming upgrades and cooldowns
36// shouldn't exceed this number.
37const SAMPLE_SIZE: u32 = 1024;
38
39fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
40	let events = frame_system::Pallet::<T>::events();
41	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
42	// compare to the last event record
43	let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
44	assert_eq!(event, &system_event);
45}
46
47fn generate_disordered_pruning<T: Config>() {
48	let mut needs_pruning = Vec::new();
49
50	for i in 0..SAMPLE_SIZE {
51		let id = ParaId::from(i);
52		let block_number = BlockNumberFor::<T>::from(1000u32);
53		needs_pruning.push((id, block_number));
54	}
55
56	PastCodePruning::<T>::put(needs_pruning);
57}
58
59pub(crate) fn generate_disordered_upgrades<T: Config>() {
60	let mut upgrades = Vec::new();
61	let mut cooldowns = Vec::new();
62
63	for i in 0..SAMPLE_SIZE {
64		let id = ParaId::from(i);
65		let block_number = BlockNumberFor::<T>::from(1000u32);
66		upgrades.push((id, block_number));
67		cooldowns.push((id, block_number));
68	}
69
70	UpcomingUpgrades::<T>::put(upgrades);
71	UpgradeCooldowns::<T>::put(cooldowns);
72}
73
74fn generate_disordered_actions_queue<T: Config>() {
75	let mut queue = Vec::new();
76	let next_session = shared::CurrentSessionIndex::<T>::get().saturating_add(One::one());
77
78	for _ in 0..SAMPLE_SIZE {
79		let id = ParaId::from(1000);
80		queue.push(id);
81	}
82
83	ActionsQueue::<T>::mutate(next_session, |v| {
84		*v = queue;
85	});
86}
87
88#[benchmarks]
89mod benchmarks {
90	use super::*;
91
92	#[benchmark]
93	fn force_set_current_code(c: Linear<MIN_CODE_SIZE, MAX_CODE_SIZE>) {
94		let new_code = ValidationCode(vec![0; c as usize]);
95		let para_id = ParaId::from(c as u32);
96		CurrentCodeHash::<T>::insert(&para_id, new_code.hash());
97		generate_disordered_pruning::<T>();
98
99		#[extrinsic_call]
100		_(RawOrigin::Root, para_id, new_code);
101
102		assert_last_event::<T>(Event::CurrentCodeUpdated(para_id).into());
103	}
104
105	#[benchmark]
106	fn force_set_current_head(s: Linear<MIN_CODE_SIZE, MAX_HEAD_DATA_SIZE>) {
107		let new_head = HeadData(vec![0; s as usize]);
108		let para_id = ParaId::from(1000);
109
110		#[extrinsic_call]
111		_(RawOrigin::Root, para_id, new_head);
112
113		assert_last_event::<T>(Event::CurrentHeadUpdated(para_id).into());
114	}
115
116	#[benchmark]
117	fn force_set_most_recent_context() {
118		let para_id = ParaId::from(1000);
119		let context = BlockNumberFor::<T>::from(1000u32);
120
121		#[extrinsic_call]
122		_(RawOrigin::Root, para_id, context);
123	}
124
125	#[benchmark]
126	fn force_schedule_code_upgrade(c: Linear<MIN_CODE_SIZE, MAX_CODE_SIZE>) {
127		let new_code = ValidationCode(vec![0; c as usize]);
128		let para_id = ParaId::from(c as u32);
129		let block = BlockNumberFor::<T>::from(c);
130		generate_disordered_upgrades::<T>();
131
132		#[extrinsic_call]
133		_(RawOrigin::Root, para_id, new_code, block);
134
135		assert_last_event::<T>(Event::CodeUpgradeScheduled(para_id).into());
136	}
137
138	#[benchmark]
139	fn force_note_new_head(s: Linear<MIN_CODE_SIZE, MAX_HEAD_DATA_SIZE>) {
140		let para_id = ParaId::from(1000);
141		let new_head = HeadData(vec![0; s as usize]);
142		let old_code_hash = ValidationCode(vec![0]).hash();
143		CurrentCodeHash::<T>::insert(&para_id, old_code_hash);
144		frame_system::Pallet::<T>::set_block_number(10u32.into());
145		// schedule an expired code upgrade for this `para_id` so that force_note_new_head would use
146		// the worst possible code path
147		let expired = frame_system::Pallet::<T>::block_number().saturating_sub(One::one());
148		let config = HostConfiguration::<BlockNumberFor<T>>::default();
149		generate_disordered_pruning::<T>();
150		Pallet::<T>::schedule_code_upgrade(
151			para_id,
152			ValidationCode(vec![0u8; MIN_CODE_SIZE as usize]),
153			expired,
154			&config,
155			UpgradeStrategy::SetGoAheadSignal,
156		);
157
158		#[extrinsic_call]
159		_(RawOrigin::Root, para_id, new_head);
160
161		assert_last_event::<T>(Event::NewHeadNoted(para_id).into());
162	}
163
164	#[benchmark]
165	fn force_queue_action() {
166		let para_id = ParaId::from(1000);
167		generate_disordered_actions_queue::<T>();
168
169		#[extrinsic_call]
170		_(RawOrigin::Root, para_id);
171
172		let next_session =
173			crate::shared::CurrentSessionIndex::<T>::get().saturating_add(One::one());
174		assert_last_event::<T>(Event::ActionQueued(para_id, next_session).into());
175	}
176
177	#[benchmark]
178	fn add_trusted_validation_code(c: Linear<MIN_CODE_SIZE, MAX_CODE_SIZE>) {
179		let new_code = ValidationCode(vec![0; c as usize]);
180
181		pvf_check::prepare_bypassing_bench::<T>(new_code.clone());
182
183		#[extrinsic_call]
184		_(RawOrigin::Root, new_code);
185	}
186
187	#[benchmark]
188	fn poke_unused_validation_code() {
189		let code_hash = [0; 32].into();
190
191		#[extrinsic_call]
192		_(RawOrigin::Root, code_hash);
193	}
194
195	#[benchmark]
196	fn include_pvf_check_statement() {
197		let (stmt, signature) = pvf_check::prepare_inclusion_bench::<T>();
198
199		#[block]
200		{
201			let _ =
202				Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, signature);
203		}
204	}
205
206	#[benchmark]
207	fn include_pvf_check_statement_finalize_upgrade_accept() {
208		let (stmt, signature) =
209			pvf_check::prepare_finalization_bench::<T>(VoteCause::Upgrade, VoteOutcome::Accept);
210
211		#[block]
212		{
213			let _ =
214				Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, signature);
215		}
216	}
217
218	#[benchmark]
219	fn include_pvf_check_statement_finalize_upgrade_reject() {
220		let (stmt, signature) =
221			pvf_check::prepare_finalization_bench::<T>(VoteCause::Upgrade, VoteOutcome::Reject);
222
223		#[block]
224		{
225			let _ =
226				Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, signature);
227		}
228	}
229
230	#[benchmark]
231	fn include_pvf_check_statement_finalize_onboarding_accept() {
232		let (stmt, signature) =
233			pvf_check::prepare_finalization_bench::<T>(VoteCause::Onboarding, VoteOutcome::Accept);
234
235		#[block]
236		{
237			let _ =
238				Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, signature);
239		}
240	}
241
242	#[benchmark]
243	fn include_pvf_check_statement_finalize_onboarding_reject() {
244		let (stmt, signature) =
245			pvf_check::prepare_finalization_bench::<T>(VoteCause::Onboarding, VoteOutcome::Reject);
246
247		#[block]
248		{
249			let _ =
250				Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, signature);
251		}
252	}
253
254	#[benchmark]
255	fn remove_upgrade_cooldown() -> Result<(), BenchmarkError> {
256		let para_id = ParaId::from(1000);
257		let old_code_hash = ValidationCode(vec![0]).hash();
258		CurrentCodeHash::<T>::insert(&para_id, old_code_hash);
259		frame_system::Pallet::<T>::set_block_number(10u32.into());
260		let inclusion = frame_system::Pallet::<T>::block_number().saturating_add(10u32.into());
261		let config = HostConfiguration::<BlockNumberFor<T>>::default();
262		Pallet::<T>::schedule_code_upgrade(
263			para_id,
264			ValidationCode(vec![0u8; MIN_CODE_SIZE as usize]),
265			inclusion,
266			&config,
267			UpgradeStrategy::SetGoAheadSignal,
268		);
269
270		let who: T::AccountId = whitelisted_caller();
271
272		T::Fungible::mint_into(
273			&who,
274			T::CooldownRemovalMultiplier::get().saturating_mul(1_000_000u32.into()),
275		)?;
276
277		#[extrinsic_call]
278		_(RawOrigin::Signed(who), para_id);
279
280		assert_last_event::<T>(Event::UpgradeCooldownRemoved { para_id }.into());
281
282		Ok(())
283	}
284
285	#[benchmark]
286	fn authorize_force_set_current_code_hash() {
287		let para_id = ParaId::from(1000);
288		let code = ValidationCode(vec![0; 32]);
289		let new_code_hash = code.hash();
290		let valid_period = BlockNumberFor::<T>::from(1_000_000_u32);
291		ParaLifecycles::<T>::insert(&para_id, ParaLifecycle::Parachain);
292
293		#[extrinsic_call]
294		_(RawOrigin::Root, para_id, new_code_hash, valid_period);
295
296		assert_last_event::<T>(
297			Event::CodeAuthorized {
298				para_id,
299				code_hash: new_code_hash,
300				expire_at: frame_system::Pallet::<T>::block_number().saturating_add(valid_period),
301			}
302			.into(),
303		);
304	}
305
306	#[benchmark]
307	fn apply_authorized_force_set_current_code(c: Linear<MIN_CODE_SIZE, MAX_CODE_SIZE>) {
308		let code = ValidationCode(vec![0; c as usize]);
309		let para_id = ParaId::from(1000);
310		let expire_at =
311			frame_system::Pallet::<T>::block_number().saturating_add(BlockNumberFor::<T>::from(c));
312		AuthorizedCodeHash::<T>::insert(
313			&para_id,
314			AuthorizedCodeHashAndExpiry::from((code.hash(), expire_at)),
315		);
316		generate_disordered_pruning::<T>();
317
318		#[extrinsic_call]
319		_(RawOrigin::Root, para_id, code);
320
321		assert_last_event::<T>(Event::CurrentCodeUpdated(para_id).into());
322	}
323
324	impl_benchmark_test_suite!(
325		Pallet,
326		crate::mock::new_test_ext(Default::default()),
327		crate::mock::Test
328	);
329}