referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/paras/benchmarking/
pvf_check.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//! This module focuses on the benchmarking of the `include_pvf_check_statement` dispatchable.
18
19use crate::{configuration, paras::*, shared::Pallet as ParasShared};
20use alloc::{vec, vec::Vec};
21use frame_support::assert_ok;
22use frame_system::RawOrigin;
23use polkadot_primitives::{HeadData, Id as ParaId, ValidationCode, ValidatorId, ValidatorIndex};
24use sp_application_crypto::RuntimeAppPublic;
25
26// Constants for the benchmarking
27const SESSION_INDEX: SessionIndex = 1;
28const VALIDATOR_NUM: usize = 800;
29const CAUSES_NUM: usize = 100;
30fn validation_code() -> ValidationCode {
31	ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9])
32}
33fn old_validation_code() -> ValidationCode {
34	ValidationCode(vec![9, 8, 7, 6, 5, 4, 3, 2, 1])
35}
36
37/// Prepares the PVF check statement and the validator signature to pass into
38/// `include_pvf_check_statement` during benchmarking phase.
39///
40/// It won't trigger finalization, so we expect the benchmarking will only measure the performance
41/// of only vote accounting.
42pub fn prepare_inclusion_bench<T>() -> (PvfCheckStatement, ValidatorSignature)
43where
44	T: Config + shared::Config,
45{
46	initialize::<T>();
47	// we do not plan to trigger finalization, thus the cause is inconsequential.
48	initialize_pvf_active_vote::<T>(VoteCause::Onboarding, CAUSES_NUM);
49
50	// `unwrap` cannot panic here since the `initialize` function should initialize validators count
51	// to be more than 0.
52	//
53	// VoteDirection doesn't matter here as well.
54	let stmt_n_sig = generate_statements::<T>(VoteOutcome::Accept).next().unwrap();
55
56	stmt_n_sig
57}
58
59/// Prepares conditions for benchmarking of the finalization part of `include_pvf_check_statement`.
60///
61/// This function will initialize a PVF pre-check vote, then submit a number of PVF pre-checking
62/// statements so that to achieve the quorum only one statement is left. This statement is returned
63/// from this function and is expected to be passed into `include_pvf_check_statement` during the
64/// benchmarking phase.
65pub fn prepare_finalization_bench<T>(
66	cause: VoteCause,
67	outcome: VoteOutcome,
68) -> (PvfCheckStatement, ValidatorSignature)
69where
70	T: Config + shared::Config,
71{
72	initialize::<T>();
73	initialize_pvf_active_vote::<T>(cause, CAUSES_NUM);
74
75	let mut stmts = generate_statements::<T>(outcome).collect::<Vec<_>>();
76	// this should be ensured by the `initialize` function.
77	assert!(stmts.len() > 2);
78
79	// stash the last statement to be used in the benchmarking phase.
80	let stmt_n_sig = stmts.pop().unwrap();
81
82	for (stmt, sig) in stmts {
83		let r = Pallet::<T>::include_pvf_check_statement(RawOrigin::None.into(), stmt, sig);
84		assert!(r.is_ok());
85	}
86
87	stmt_n_sig
88}
89
90/// Prepares storage for invoking `add_trusted_validation_code` with several paras initializing to
91/// the same code.
92pub fn prepare_bypassing_bench<T>(validation_code: ValidationCode)
93where
94	T: Config + shared::Config,
95{
96	// Suppose a sensible number of paras initialize to the same code.
97	const PARAS_NUM: usize = 10;
98
99	initialize::<T>();
100	for i in 0..PARAS_NUM {
101		let id = ParaId::from(i as u32);
102		assert_ok!(Pallet::<T>::schedule_para_initialize(
103			id,
104			ParaGenesisArgs {
105				para_kind: ParaKind::Parachain,
106				genesis_head: HeadData(vec![1, 2, 3, 4]),
107				validation_code: validation_code.clone(),
108			},
109		));
110	}
111}
112
113/// What caused the PVF pre-checking vote?
114#[derive(PartialEq, Eq, Copy, Clone, Debug)]
115pub enum VoteCause {
116	Onboarding,
117	Upgrade,
118}
119
120/// The outcome of the PVF pre-checking vote.
121#[derive(PartialEq, Eq, Copy, Clone, Debug)]
122pub enum VoteOutcome {
123	Accept,
124	Reject,
125}
126
127fn initialize<T>()
128where
129	T: Config + shared::Config,
130{
131	// 0. generate a list of validators
132	let validators = (0..VALIDATOR_NUM)
133		.map(|_| <ValidatorId as RuntimeAppPublic>::generate_pair(None))
134		.collect::<Vec<_>>();
135
136	// 1. Make sure PVF pre-checking is enabled in the config.
137	let config = configuration::ActiveConfig::<T>::get();
138	configuration::Pallet::<T>::force_set_active_config(config.clone());
139
140	// 2. initialize a new session with deterministic validator set.
141	ParasShared::<T>::set_active_validators_ascending(validators.clone());
142	ParasShared::<T>::set_session_index(SESSION_INDEX);
143}
144
145/// Creates a new PVF pre-checking active vote.
146///
147/// The subject of the vote (i.e. validation code) and the cause (upgrade/onboarding) is specified
148/// by the test setup.
149fn initialize_pvf_active_vote<T>(vote_cause: VoteCause, causes_num: usize)
150where
151	T: Config + shared::Config,
152{
153	for i in 0..causes_num {
154		let id = ParaId::from(i as u32);
155
156		if vote_cause == VoteCause::Upgrade {
157			// we do care about validation code being actually different, since there is a check
158			// that prevents upgrading to the same code.
159			let old_validation_code = old_validation_code();
160			let validation_code = validation_code();
161
162			let mut parachains = ParachainsCache::new();
163			Pallet::<T>::initialize_para_now(
164				&mut parachains,
165				id,
166				&ParaGenesisArgs {
167					para_kind: ParaKind::Parachain,
168					genesis_head: HeadData(vec![1, 2, 3, 4]),
169					validation_code: old_validation_code,
170				},
171			);
172			// don't care about performance here, but we do care about robustness. So dump the cache
173			// asap.
174			drop(parachains);
175
176			Pallet::<T>::schedule_code_upgrade(
177				id,
178				validation_code,
179				/* relay_parent_number */ 1u32.into(),
180				&configuration::ActiveConfig::<T>::get(),
181				UpgradeStrategy::SetGoAheadSignal,
182			);
183		} else {
184			let r = Pallet::<T>::schedule_para_initialize(
185				id,
186				ParaGenesisArgs {
187					para_kind: ParaKind::Parachain,
188					genesis_head: HeadData(vec![1, 2, 3, 4]),
189					validation_code: validation_code(),
190				},
191			);
192			assert!(r.is_ok());
193		}
194	}
195}
196
197/// Generates a list of votes combined with signatures for the active validator set. The number of
198/// votes is equal to the minimum number of votes required to reach the threshold for either accept
199/// or reject.
200fn generate_statements<T>(
201	vote_outcome: VoteOutcome,
202) -> impl Iterator<Item = (PvfCheckStatement, ValidatorSignature)>
203where
204	T: Config + shared::Config,
205{
206	let validators = shared::ActiveValidatorKeys::<T>::get();
207
208	let accept_threshold = polkadot_primitives::supermajority_threshold(validators.len());
209	let required_votes = match vote_outcome {
210		VoteOutcome::Accept => accept_threshold,
211		VoteOutcome::Reject => validators.len() - accept_threshold,
212	};
213	(0..required_votes).map(move |validator_index| {
214		let stmt = PvfCheckStatement {
215			accept: vote_outcome == VoteOutcome::Accept,
216			subject: validation_code().hash(),
217			session_index: SESSION_INDEX,
218
219			validator_index: ValidatorIndex(validator_index as u32),
220		};
221		let signature = validators[validator_index].sign(&stmt.signing_payload()).unwrap();
222
223		(stmt, signature)
224	})
225}