referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/
builder.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 crate::{
18	configuration, inclusion, initializer, paras,
19	paras::ParaKind,
20	paras_inherent,
21	scheduler::{
22		self,
23		common::{Assignment, AssignmentProvider},
24	},
25	session_info, shared,
26};
27use alloc::{
28	collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque},
29	vec,
30	vec::Vec,
31};
32use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
33use frame_support::pallet_prelude::*;
34use frame_system::pallet_prelude::*;
35use polkadot_primitives::{
36	ApprovedPeerId, AvailabilityBitfield, BackedCandidate, CandidateCommitments,
37	CandidateDescriptorV2, CandidateHash, ClaimQueueOffset,
38	CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CompactStatement, CoreIndex,
39	CoreSelector, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, Id as ParaId,
40	IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind,
41	PersistedValidationData, SessionIndex, SigningContext, UMPSignal, UncheckedSigned,
42	ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation,
43	UMP_SEPARATOR,
44};
45use sp_core::H256;
46use sp_runtime::{
47	generic::Digest,
48	traits::{Header as HeaderT, One, TrailingZeroInput, Zero},
49	RuntimeAppPublic,
50};
51fn mock_validation_code() -> ValidationCode {
52	ValidationCode(vec![1, 2, 3])
53}
54
55/// Grab an account, seeded by a name and index.
56///
57/// This is directly from frame-benchmarking. Copy/pasted so we can use it when not compiling with
58/// "features = runtime-benchmarks".
59fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
60	let entropy = (name, index, seed).using_encoded(sp_io::hashing::blake2_256);
61	AccountId::decode(&mut TrailingZeroInput::new(&entropy[..]))
62		.expect("infinite input; no invalid input; qed")
63}
64
65pub fn generate_validator_pairs<T: frame_system::Config>(
66	validator_count: u32,
67) -> Vec<(T::AccountId, ValidatorId)> {
68	(0..validator_count)
69		.map(|i| {
70			let public = ValidatorId::generate_pair(None);
71
72			// The account Id is not actually used anywhere, just necessary to fulfill the
73			// expected type of the `validators` param of `test_trigger_on_new_session`.
74			let account: T::AccountId = account("validator", i, i);
75			(account, public)
76		})
77		.collect()
78}
79
80/// Create a 32 byte slice based on the given number.
81fn byte32_slice_from(n: u32) -> [u8; 32] {
82	let mut slice = [0u8; 32];
83	slice[31] = (n % (1 << 8)) as u8;
84	slice[30] = ((n >> 8) % (1 << 8)) as u8;
85	slice[29] = ((n >> 16) % (1 << 8)) as u8;
86	slice[28] = ((n >> 24) % (1 << 8)) as u8;
87
88	slice
89}
90
91/// Paras inherent `enter` benchmark scenario builder.
92pub(crate) struct BenchBuilder<T: paras_inherent::Config> {
93	/// Active validators. Validators should be declared prior to all other setup.
94	validators: Option<IndexedVec<ValidatorIndex, ValidatorId>>,
95	/// Starting block number; we expect it to get incremented on session setup.
96	block_number: BlockNumberFor<T>,
97	/// Starting session; we expect it to get incremented on session setup.
98	session: SessionIndex,
99	/// Session we want the scenario to take place in. We will roll to this session.
100	target_session: u32,
101	/// Optionally set the max validators per core; otherwise uses the configuration value.
102	max_validators_per_core: Option<u32>,
103	/// Optionally set the max validators; otherwise uses the configuration value.
104	max_validators: Option<u32>,
105	/// Optionally set the number of dispute statements for each candidate.
106	dispute_statements: BTreeMap<u32, u32>,
107	/// Session index of for each dispute. Index of slice corresponds to a core,
108	/// which is offset by the number of entries for `backed_and_concluding_paras`. I.E. if
109	/// `backed_and_concluding_paras` has 3 entries, the first index of `dispute_sessions`
110	/// will correspond to core index 3. There must be one entry for each core with a dispute
111	/// statement set.
112	dispute_sessions: Vec<u32>,
113	/// Paras here will both be backed in the inherent data and already occupying a core (which is
114	/// freed via bitfields).
115	///
116	/// Map from para id to number of validity votes. Core indices are generated based on
117	/// `elastic_paras` configuration. Each para id in `elastic_paras` gets the
118	/// specified amount of consecutive cores assigned to it. If a para id is not present
119	/// in `elastic_paras` it get assigned to a single core.
120	backed_and_concluding_paras: BTreeMap<u32, u32>,
121
122	/// Paras which don't yet occupy a core, but will after the inherent has been processed.
123	backed_in_inherent_paras: BTreeMap<u32, u32>,
124	/// Map from para id (seed) to number of chained candidates.
125	elastic_paras: BTreeMap<u32, u8>,
126	/// Make every candidate include a code upgrade by setting this to `Some` where the interior
127	/// value is the byte length of the new code.
128	code_upgrade: Option<u32>,
129	/// Cores which should not be available when being populated with pending candidates.
130	unavailable_cores: Vec<u32>,
131	/// Use v2 candidate descriptor.
132	candidate_descriptor_v2: bool,
133	/// Send an approved peer ump signal. Only useful for v2 descriptors
134	approved_peer_signal: Option<ApprovedPeerId>,
135	/// Apply custom changes to generated candidates
136	candidate_modifier: Option<CandidateModifier<T::Hash>>,
137	_phantom: core::marker::PhantomData<T>,
138}
139
140pub type CandidateModifier<Hash> =
141	fn(CommittedCandidateReceipt<Hash>) -> CommittedCandidateReceipt<Hash>;
142
143/// Paras inherent `enter` benchmark scenario.
144#[cfg(any(feature = "runtime-benchmarks", test))]
145pub(crate) struct Bench<T: paras_inherent::Config> {
146	pub(crate) data: ParachainsInherentData<HeaderFor<T>>,
147	pub(crate) _session: u32,
148	pub(crate) _block_number: BlockNumberFor<T>,
149}
150
151#[allow(dead_code)]
152impl<T: paras_inherent::Config> BenchBuilder<T> {
153	/// Create a new `BenchBuilder` with some opinionated values that should work with the rest
154	/// of the functions in this implementation.
155	pub(crate) fn new() -> Self {
156		BenchBuilder {
157			validators: None,
158			block_number: Zero::zero(),
159			session: SessionIndex::from(0u32),
160			target_session: 2u32,
161			max_validators_per_core: None,
162			max_validators: None,
163			dispute_statements: BTreeMap::new(),
164			dispute_sessions: Default::default(),
165			backed_and_concluding_paras: Default::default(),
166			backed_in_inherent_paras: Default::default(),
167			elastic_paras: Default::default(),
168			code_upgrade: None,
169			unavailable_cores: vec![],
170			candidate_descriptor_v2: false,
171			approved_peer_signal: None,
172			candidate_modifier: None,
173			_phantom: core::marker::PhantomData::<T>,
174		}
175	}
176
177	/// Set the session index for each dispute statement set (in other words, set the session the
178	/// the dispute statement set's relay chain block is from). Indexes of `dispute_sessions`
179	/// correspond to a core, which is offset by the number of entries for
180	/// `backed_and_concluding_paras`. I.E. if `backed_and_concluding_paras` cores has 3 entries,
181	/// the first index of `dispute_sessions` will correspond to core index 3.
182	///
183	/// Note that there must be an entry for each core with a dispute statement set.
184	pub(crate) fn set_dispute_sessions(mut self, dispute_sessions: impl AsRef<[u32]>) -> Self {
185		self.dispute_sessions = dispute_sessions.as_ref().to_vec();
186		self
187	}
188
189	/// Set the cores which should not be available when being populated with pending candidates.
190	pub(crate) fn set_unavailable_cores(mut self, unavailable_cores: Vec<u32>) -> Self {
191		self.unavailable_cores = unavailable_cores;
192		self
193	}
194
195	/// Set a map from para id seed to number of validity votes.
196	pub(crate) fn set_backed_and_concluding_paras(
197		mut self,
198		backed_and_concluding_paras: BTreeMap<u32, u32>,
199	) -> Self {
200		self.backed_and_concluding_paras = backed_and_concluding_paras;
201		self
202	}
203
204	/// Set a map from para id seed to number of validity votes for votes in inherent data.
205	pub(crate) fn set_backed_in_inherent_paras(mut self, backed: BTreeMap<u32, u32>) -> Self {
206		self.backed_in_inherent_paras = backed;
207		self
208	}
209
210	/// Set a map from para id seed to number of cores assigned to it.
211	pub(crate) fn set_elastic_paras(mut self, elastic_paras: BTreeMap<u32, u8>) -> Self {
212		self.elastic_paras = elastic_paras;
213		self
214	}
215
216	/// Set to include a code upgrade for all backed candidates. The value will be the byte length
217	/// of the code.
218	pub(crate) fn set_code_upgrade(mut self, code_upgrade: impl Into<Option<u32>>) -> Self {
219		self.code_upgrade = code_upgrade.into();
220		self
221	}
222
223	/// Mock header.
224	pub(crate) fn header(block_number: BlockNumberFor<T>) -> HeaderFor<T> {
225		HeaderFor::<T>::new(
226			block_number,       // `block_number`,
227			Default::default(), // `extrinsics_root`,
228			Default::default(), // `storage_root`,
229			Default::default(), // `parent_hash`,
230			Default::default(), // digest,
231		)
232	}
233
234	/// Number of the relay parent block.
235	fn relay_parent_number(&self) -> u32 {
236		(self.block_number - One::one())
237			.try_into()
238			.map_err(|_| ())
239			.expect("self.block_number is u32")
240	}
241
242	/// Fallback for the maximum number of validators participating in parachains consensus (a.k.a.
243	/// active validators).
244	pub(crate) fn fallback_max_validators() -> u32 {
245		configuration::ActiveConfig::<T>::get().max_validators.unwrap_or(1024)
246	}
247
248	/// Maximum number of validators participating in parachains consensus (a.k.a. active
249	/// validators).
250	fn max_validators(&self) -> u32 {
251		self.max_validators.unwrap_or(Self::fallback_max_validators())
252	}
253
254	/// Set the maximum number of active validators.
255	#[cfg(not(feature = "runtime-benchmarks"))]
256	pub(crate) fn set_max_validators(mut self, n: u32) -> Self {
257		self.max_validators = Some(n);
258		self
259	}
260
261	/// Maximum number of validators per core (a.k.a. max validators per group). This value is used
262	/// if none is explicitly set on the builder.
263	pub(crate) fn fallback_max_validators_per_core() -> u32 {
264		configuration::ActiveConfig::<T>::get()
265			.scheduler_params
266			.max_validators_per_core
267			.unwrap_or(5)
268	}
269
270	/// Specify a mapping of core index/ para id to the number of dispute statements for the
271	/// corresponding dispute statement set. Note that if the number of disputes is not specified
272	/// it fallbacks to having a dispute per every validator. Additionally, an entry is not
273	/// guaranteed to have a dispute - it must line up with the cores marked as disputed as defined
274	/// in `Self::Build`.
275	#[cfg(not(feature = "runtime-benchmarks"))]
276	pub(crate) fn set_dispute_statements(mut self, m: BTreeMap<u32, u32>) -> Self {
277		self.dispute_statements = m;
278		self
279	}
280
281	/// Toggle usage of v2 candidate descriptors.
282	pub(crate) fn set_candidate_descriptor_v2(mut self, enable: bool) -> Self {
283		self.candidate_descriptor_v2 = enable;
284		self
285	}
286
287	/// Set an approved peer to be sent as a UMP signal. Only used for v2 descriptors
288	pub(crate) fn set_approved_peer_signal(mut self, peer_id: ApprovedPeerId) -> Self {
289		self.approved_peer_signal = Some(peer_id);
290		self
291	}
292
293	/// Set the candidate modifier.
294	pub(crate) fn set_candidate_modifier(
295		mut self,
296		modifier: Option<CandidateModifier<T::Hash>>,
297	) -> Self {
298		self.candidate_modifier = modifier;
299		self
300	}
301
302	/// Get the maximum number of validators per core.
303	fn max_validators_per_core(&self) -> u32 {
304		self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core())
305	}
306
307	/// Set maximum number of validators per core.
308	#[cfg(not(feature = "runtime-benchmarks"))]
309	pub(crate) fn set_max_validators_per_core(mut self, n: u32) -> Self {
310		self.max_validators_per_core = Some(n);
311		self
312	}
313
314	/// Get the maximum number of cores we expect from this configuration.
315	pub(crate) fn max_cores(&self) -> u32 {
316		self.max_validators() / self.max_validators_per_core()
317	}
318
319	/// Get the minimum number of validity votes in order for a backed candidate to be included.
320	#[cfg(feature = "runtime-benchmarks")]
321	pub(crate) fn fallback_min_backing_votes() -> u32 {
322		2
323	}
324
325	fn mock_head_data() -> HeadData {
326		let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
327		HeadData(vec![0xFF; max_head_size as usize])
328	}
329
330	fn candidate_descriptor_mock(para_id: ParaId) -> CandidateDescriptorV2<T::Hash> {
331		CandidateDescriptorV2::new(
332			para_id,
333			Default::default(),
334			CoreIndex(200),
335			2,
336			Default::default(),
337			Default::default(),
338			Default::default(),
339			Default::default(),
340			mock_validation_code().hash(),
341		)
342	}
343
344	/// Create a mock of `CandidatePendingAvailability`.
345	fn candidate_availability_mock(
346		para_id: ParaId,
347		group_idx: GroupIndex,
348		core_idx: CoreIndex,
349		candidate_hash: CandidateHash,
350		availability_votes: BitVec<u8, BitOrderLsb0>,
351		commitments: CandidateCommitments,
352	) -> inclusion::CandidatePendingAvailability<T::Hash, BlockNumberFor<T>> {
353		inclusion::CandidatePendingAvailability::<T::Hash, BlockNumberFor<T>>::new(
354			core_idx,                                 // core
355			candidate_hash,                           // hash
356			Self::candidate_descriptor_mock(para_id), /* candidate descriptor */
357			commitments,                              // commitments
358			availability_votes,                       /* availability
359			                                           * votes */
360			Default::default(), // backers
361			Zero::zero(),       // relay parent
362			One::one(),         /* relay chain block this
363			                     * was backed in */
364			group_idx, // backing group
365		)
366	}
367
368	/// Add `CandidatePendingAvailability` and `CandidateCommitments` to the relevant storage items.
369	///
370	/// NOTE: the default `CandidateCommitments` used does not include any data that would lead to
371	/// heavy code paths in `enact_candidate`. But enact_candidates does return a weight which will
372	/// get taken into account.
373	fn add_availability(
374		para_id: ParaId,
375		core_idx: CoreIndex,
376		group_idx: GroupIndex,
377		availability_votes: BitVec<u8, BitOrderLsb0>,
378		candidate_hash: CandidateHash,
379	) {
380		let commitments = CandidateCommitments::<u32> {
381			upward_messages: Default::default(),
382			horizontal_messages: Default::default(),
383			new_validation_code: None,
384			head_data: Self::mock_head_data(),
385			processed_downward_messages: 0,
386			hrmp_watermark: 0u32.into(),
387		};
388		let candidate_availability = Self::candidate_availability_mock(
389			para_id,
390			group_idx,
391			core_idx,
392			candidate_hash,
393			availability_votes,
394			commitments,
395		);
396		inclusion::PendingAvailability::<T>::mutate(para_id, |maybe_candidates| {
397			if let Some(candidates) = maybe_candidates {
398				candidates.push_back(candidate_availability);
399			} else {
400				*maybe_candidates =
401					Some([candidate_availability].into_iter().collect::<VecDeque<_>>());
402			}
403		});
404	}
405
406	/// Create an `AvailabilityBitfield` where `concluding` is a map where each key is a core index
407	/// that is concluding and `cores` is the total number of cores in the system.
408	fn availability_bitvec(concluding_cores: &BTreeSet<u32>, cores: usize) -> AvailabilityBitfield {
409		let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0];
410		for i in 0..cores {
411			if concluding_cores.contains(&(i as u32)) {
412				bitfields.push(true);
413			} else {
414				bitfields.push(false)
415			}
416		}
417
418		bitfields.into()
419	}
420
421	/// Run to block number `to`, calling `initializer` `on_initialize` and `on_finalize` along the
422	/// way.
423	fn run_to_block(to: u32) {
424		let to = to.into();
425		while frame_system::Pallet::<T>::block_number() < to {
426			let b = frame_system::Pallet::<T>::block_number();
427			initializer::Pallet::<T>::on_finalize(b);
428
429			let b = b + One::one();
430			frame_system::Pallet::<T>::set_block_number(b);
431			initializer::Pallet::<T>::on_initialize(b);
432		}
433	}
434
435	/// Register `n_paras` count of parachains.
436	///
437	/// Note that this must be called at least 2 sessions before the target session as there is a
438	/// n+2 session delay for the scheduled actions to take effect.
439	fn setup_para_ids(n_paras: usize) {
440		// make sure parachains exist prior to session change.
441		for i in 0..n_paras {
442			let para_id = ParaId::from(i as u32);
443			let validation_code = mock_validation_code();
444
445			paras::Pallet::<T>::schedule_para_initialize(
446				para_id,
447				paras::ParaGenesisArgs {
448					genesis_head: Self::mock_head_data(),
449					validation_code: validation_code.clone(),
450					para_kind: ParaKind::Parachain,
451				},
452			)
453			.unwrap();
454			paras::Pallet::<T>::add_trusted_validation_code(
455				frame_system::Origin::<T>::Root.into(),
456				validation_code,
457			)
458			.unwrap();
459		}
460	}
461
462	fn signing_context(&self) -> SigningContext<T::Hash> {
463		SigningContext {
464			parent_hash: Self::header(self.block_number).hash(),
465			session_index: self.session,
466		}
467	}
468
469	/// Create a bitvec of `validators` length with all yes votes.
470	fn validator_availability_votes_yes(validators: usize) -> BitVec<u8, bitvec::order::Lsb0> {
471		// every validator confirms availability.
472		bitvec::bitvec![u8, bitvec::order::Lsb0; 1; validators as usize]
473	}
474
475	/// Setup session 1 and create `self.validators_map` and `self.validators`.
476	fn setup_session(
477		mut self,
478		target_session: SessionIndex,
479		validators: Vec<(T::AccountId, ValidatorId)>,
480		// Total cores used in the scenario
481		total_cores: usize,
482		// Additional cores for elastic parachains
483		extra_cores: usize,
484	) -> Self {
485		let mut block = 1;
486		for session in 0..=target_session {
487			initializer::Pallet::<T>::test_trigger_on_new_session(
488				false,
489				session,
490				validators.iter().map(|(a, v)| (a, v.clone())),
491				None,
492			);
493			block += 1;
494			Self::run_to_block(block);
495		}
496
497		let block_number = BlockNumberFor::<T>::from(block);
498		let header = Self::header(block_number);
499
500		frame_system::Pallet::<T>::reset_events();
501		frame_system::Pallet::<T>::initialize(
502			&header.number(),
503			&header.hash(),
504			&Digest { logs: Vec::new() },
505		);
506
507		assert_eq!(shared::CurrentSessionIndex::<T>::get(), target_session);
508
509		// We need to refetch validators since they have been shuffled.
510		let validators_shuffled =
511			session_info::Sessions::<T>::get(target_session).unwrap().validators.clone();
512
513		self.validators = Some(validators_shuffled);
514		self.block_number = block_number;
515		self.session = target_session;
516		assert_eq!(paras::Parachains::<T>::get().len(), total_cores - extra_cores);
517
518		self
519	}
520
521	/// Create a `UncheckedSigned<AvailabilityBitfield> for each validator where each core in
522	/// `concluding_cores` is fully available. Additionally set up storage such that each
523	/// `concluding_cores`is pending becoming fully available so the generated bitfields will be
524	///  to the cores successfully being freed from the candidates being marked as available.
525	fn create_availability_bitfields(
526		&self,
527		concluding_paras: &BTreeMap<u32, u32>,
528		elastic_paras: &BTreeMap<u32, u8>,
529		total_cores: usize,
530	) -> Vec<UncheckedSigned<AvailabilityBitfield>> {
531		let validators =
532			self.validators.as_ref().expect("must have some validators prior to calling");
533
534		let mut current_core_idx = 0u32;
535		let mut concluding_cores = BTreeSet::new();
536
537		for (seed, _) in concluding_paras.iter() {
538			// make sure the candidates that will be concluding are marked as pending availability.
539			let para_id = ParaId::from(*seed);
540
541			for _chain_idx in 0..elastic_paras.get(&seed).cloned().unwrap_or(1) {
542				let core_idx = CoreIndex::from(current_core_idx);
543				let group_idx =
544					scheduler::Pallet::<T>::group_assigned_to_core(core_idx, self.block_number)
545						.unwrap();
546
547				Self::add_availability(
548					para_id,
549					core_idx,
550					group_idx,
551					// No validators have made this candidate available yet.
552					bitvec::bitvec![u8, bitvec::order::Lsb0; 0; validators.len()],
553					CandidateHash(H256::from(byte32_slice_from(current_core_idx))),
554				);
555				if !self.unavailable_cores.contains(&current_core_idx) {
556					concluding_cores.insert(current_core_idx);
557				}
558				current_core_idx += 1;
559			}
560		}
561
562		let availability_bitvec = Self::availability_bitvec(&concluding_cores, total_cores);
563
564		let bitfields: Vec<UncheckedSigned<AvailabilityBitfield>> = validators
565			.iter()
566			.enumerate()
567			.map(|(i, public)| {
568				let unchecked_signed = UncheckedSigned::<AvailabilityBitfield>::benchmark_sign(
569					public,
570					availability_bitvec.clone(),
571					&self.signing_context(),
572					ValidatorIndex(i as u32),
573				);
574
575				unchecked_signed
576			})
577			.collect();
578
579		bitfields
580	}
581
582	/// Create backed candidates for `cores_with_backed_candidates`. You need these cores to be
583	/// scheduled _within_ paras inherent, which requires marking the available bitfields as fully
584	/// available.
585	/// - `cores_with_backed_candidates` Mapping of `para_id` seed to number of
586	/// validity votes.
587	fn create_backed_candidates(
588		&self,
589		paras_with_backed_candidates: &BTreeMap<u32, u32>,
590		elastic_paras: &BTreeMap<u32, u8>,
591		includes_code_upgrade: Option<u32>,
592	) -> Vec<BackedCandidate<T::Hash>> {
593		let validators =
594			self.validators.as_ref().expect("must have some validators prior to calling");
595		let config = configuration::ActiveConfig::<T>::get();
596
597		let mut current_core_idx = 0u32;
598		paras_with_backed_candidates
599			.iter()
600			.flat_map(|(seed, num_votes)| {
601				assert!(*num_votes <= validators.len() as u32);
602
603				let para_id = ParaId::from(*seed);
604				let mut prev_head = None;
605				// How many chained candidates we want to build ?
606				(0..elastic_paras.get(&seed).cloned().unwrap_or(1))
607					.map(|chain_idx| {
608						let core_idx = CoreIndex::from(current_core_idx);
609						// Advance core index.
610						current_core_idx += 1;
611						let group_idx = scheduler::Pallet::<T>::group_assigned_to_core(
612							core_idx,
613							self.block_number,
614						)
615						.unwrap();
616
617						// This generates a pair and adds it to the keystore, returning just the
618						// public.
619						let header = Self::header(self.block_number);
620						let relay_parent = header.hash();
621
622						// Set the head data so it can be used while validating the signatures on
623						// the candidate receipt.
624						let mut head_data = Self::mock_head_data();
625
626						if chain_idx == 0 {
627							// Only first parahead of the chain needs to be set in storage.
628							paras::Pallet::<T>::heads_insert(&para_id, head_data.clone());
629						} else {
630							// Make each candidate head data unique to avoid cycles.
631							head_data.0[0] = chain_idx;
632						}
633
634						let persisted_validation_data_hash = PersistedValidationData::<H256> {
635							// To form a chain we set parent head to previous block if any, or
636							// default to what is in storage already setup.
637							parent_head: prev_head.take().unwrap_or(head_data.clone()),
638							relay_parent_number: self.relay_parent_number(),
639							relay_parent_storage_root: Default::default(),
640							max_pov_size: config.max_pov_size,
641						}
642						.hash();
643
644						prev_head = Some(head_data.clone());
645
646						let pov_hash = Default::default();
647						let validation_code_hash = mock_validation_code().hash();
648
649						let mut past_code_meta =
650							paras::ParaPastCodeMeta::<BlockNumberFor<T>>::default();
651						past_code_meta.note_replacement(0u32.into(), 0u32.into());
652
653						let group_validators =
654							scheduler::Pallet::<T>::group_validators(group_idx).unwrap();
655
656						let descriptor = CandidateDescriptorV2::new(
657							para_id,
658							relay_parent,
659							core_idx,
660							self.target_session,
661							persisted_validation_data_hash,
662							pov_hash,
663							Default::default(),
664							head_data.hash(),
665							validation_code_hash,
666						);
667
668						let mut candidate = CommittedCandidateReceipt::<T::Hash> {
669							descriptor,
670							commitments: CandidateCommitments::<u32> {
671								upward_messages: Default::default(),
672								horizontal_messages: Default::default(),
673								new_validation_code: includes_code_upgrade
674									.map(|v| ValidationCode(vec![42u8; v as usize])),
675								head_data,
676								processed_downward_messages: 0,
677								hrmp_watermark: self.relay_parent_number(),
678							},
679						};
680
681						if self.candidate_descriptor_v2 {
682							// `UMPSignal` separator.
683							candidate.commitments.upward_messages.force_push(UMP_SEPARATOR);
684
685							// `SelectCore` commitment.
686							// Claim queue offset must be `0` so this candidate is for the very
687							// next block.
688							candidate.commitments.upward_messages.force_push(
689								UMPSignal::SelectCore(
690									CoreSelector(chain_idx as u8),
691									ClaimQueueOffset(0),
692								)
693								.encode(),
694							);
695
696							if let Some(approved_peer_signal) = &self.approved_peer_signal {
697								candidate.commitments.upward_messages.force_push(
698									UMPSignal::ApprovedPeer(approved_peer_signal.clone()).encode(),
699								);
700							}
701						}
702
703						// Maybe apply the candidate modifier
704						if let Some(modifier) = self.candidate_modifier {
705							candidate = modifier(candidate);
706						}
707
708						let candidate_hash = candidate.hash();
709
710						let validity_votes: Vec<_> = group_validators
711							.iter()
712							.take(*num_votes as usize)
713							.map(|val_idx| {
714								let public = validators.get(*val_idx).unwrap();
715								let sig = UncheckedSigned::<CompactStatement>::benchmark_sign(
716									public,
717									CompactStatement::Valid(candidate_hash),
718									&self.signing_context(),
719									*val_idx,
720								)
721								.benchmark_signature();
722
723								ValidityAttestation::Explicit(sig.clone())
724							})
725							.collect();
726
727						BackedCandidate::<T::Hash>::new(
728							candidate,
729							validity_votes,
730							bitvec::bitvec![u8, bitvec::order::Lsb0; 1; group_validators.len()],
731							core_idx,
732						)
733					})
734					.collect::<Vec<_>>()
735			})
736			.collect()
737	}
738
739	/// Fill cores `start..last` with dispute statement sets. The statement sets will have 3/4th of
740	/// votes be valid, and 1/4th of votes be invalid.
741	fn create_disputes(
742		&self,
743		start: u32,
744		last: u32,
745		dispute_sessions: impl AsRef<[u32]>,
746	) -> Vec<DisputeStatementSet> {
747		let validators =
748			self.validators.as_ref().expect("must have some validators prior to calling");
749
750		let dispute_sessions = dispute_sessions.as_ref();
751		let mut current_core_idx = start;
752
753		(start..last)
754			.map(|seed| {
755				let dispute_session_idx = (seed - start) as usize;
756				let session = dispute_sessions
757					.get(dispute_session_idx)
758					.cloned()
759					.unwrap_or(self.target_session);
760
761				let para_id = ParaId::from(seed);
762				let core_idx = CoreIndex::from(current_core_idx);
763				current_core_idx +=1;
764
765				let group_idx =
766					scheduler::Pallet::<T>::group_assigned_to_core(core_idx, self.block_number)
767						.unwrap();
768
769				let candidate_hash = CandidateHash(H256::from(byte32_slice_from(seed)));
770				let relay_parent = H256::from(byte32_slice_from(seed));
771
772				Self::add_availability(
773					para_id,
774					core_idx,
775					group_idx,
776					Self::validator_availability_votes_yes(validators.len()),
777					candidate_hash,
778				);
779
780				let statements_len =
781					self.dispute_statements.get(&seed).cloned().unwrap_or(validators.len() as u32);
782				let statements = (0..statements_len)
783					.map(|validator_index| {
784						let validator_public = &validators.get(ValidatorIndex::from(validator_index)).expect("Test case is not borked. `ValidatorIndex` out of bounds of `ValidatorId`s.");
785
786						// We need dispute statements on each side. And we don't want a revert log
787						// so we make sure that we have a super majority with valid statements.
788						let dispute_statement = if validator_index % 4 == 0 {
789							DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)
790						} else if validator_index < 3 {
791							// Set two votes as backing for the dispute set to be accepted
792							DisputeStatement::Valid(
793								ValidDisputeStatementKind::BackingValid(relay_parent)
794							)
795						} else {
796							DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)
797						};
798						let data = dispute_statement.payload_data(candidate_hash, session).unwrap();
799						let statement_sig = validator_public.sign(&data).unwrap();
800
801						(dispute_statement, ValidatorIndex(validator_index), statement_sig)
802					})
803					.collect();
804
805				DisputeStatementSet { candidate_hash, session, statements }
806			})
807			.collect()
808	}
809
810	/// Build a scenario for testing or benchmarks.
811	///
812	/// Note that this API only allows building scenarios where the `backed_and_concluding_paras`
813	/// are mutually exclusive with the cores for disputes. So
814	/// `backed_and_concluding_paras.len() + dispute_sessions.len() + backed_in_inherent_paras` must
815	/// be less than the max number of cores.
816	pub(crate) fn build(self) -> Bench<T> {
817		// Make sure relevant storage is cleared. This is just to get the asserts to work when
818		// running tests because it seems the storage is not cleared in between.
819		#[allow(deprecated)]
820		inclusion::PendingAvailability::<T>::remove_all(None);
821
822		// We don't allow a core to have both disputes and be marked fully available at this block.
823		let max_cores = self.max_cores() as usize;
824
825		let extra_cores = self
826			.elastic_paras
827			.values()
828			.map(|count| *count as usize)
829			.sum::<usize>()
830			.saturating_sub(self.elastic_paras.len() as usize);
831
832		let used_cores = self.dispute_sessions.len() +
833			self.backed_and_concluding_paras.len() +
834			self.backed_in_inherent_paras.len() +
835			extra_cores;
836
837		assert!(used_cores <= max_cores);
838
839		// NOTE: there is an n+2 session delay for these actions to take effect.
840		// We are currently in Session 0, so these changes will take effect in Session 2.
841		Self::setup_para_ids(used_cores - extra_cores);
842		configuration::Pallet::<T>::set_coretime_cores_unchecked(used_cores as u32).unwrap();
843
844		let validator_ids = generate_validator_pairs::<T>(self.max_validators());
845		let target_session = SessionIndex::from(self.target_session);
846		let builder = self.setup_session(target_session, validator_ids, used_cores, extra_cores);
847
848		let bitfields = builder.create_availability_bitfields(
849			&builder.backed_and_concluding_paras,
850			&builder.elastic_paras,
851			scheduler::Pallet::<T>::num_availability_cores(),
852		);
853
854		let mut backed_in_inherent = BTreeMap::new();
855		backed_in_inherent.append(&mut builder.backed_and_concluding_paras.clone());
856		backed_in_inherent.append(&mut builder.backed_in_inherent_paras.clone());
857		let backed_candidates = builder.create_backed_candidates(
858			&backed_in_inherent,
859			&builder.elastic_paras,
860			builder.code_upgrade,
861		);
862
863		let disputes = builder.create_disputes(
864			builder.backed_and_concluding_paras.len() as u32,
865			(used_cores - extra_cores) as u32,
866			builder.dispute_sessions.as_slice(),
867		);
868		let mut disputed_cores = (builder.backed_and_concluding_paras.len() as u32..
869			((used_cores - extra_cores) as u32))
870			.into_iter()
871			.map(|idx| (idx, 0))
872			.collect::<BTreeMap<_, _>>();
873
874		let mut all_cores = builder.backed_and_concluding_paras.clone();
875		all_cores.append(&mut disputed_cores);
876
877		assert_eq!(inclusion::PendingAvailability::<T>::iter().count(), used_cores - extra_cores);
878
879		// Sanity check that the occupied cores reported by the inclusion module are what we expect
880		// to be.
881		let mut core_idx = 0u32;
882		let elastic_paras = &builder.elastic_paras;
883
884		let mut occupied_cores = inclusion::Pallet::<T>::get_occupied_cores()
885			.map(|(core, candidate)| (core, candidate.candidate_descriptor().para_id()))
886			.collect::<Vec<_>>();
887		occupied_cores.sort_by(|(core_a, _), (core_b, _)| core_a.0.cmp(&core_b.0));
888
889		let mut expected_cores = all_cores
890			.iter()
891			.flat_map(|(para_id, _)| {
892				(0..elastic_paras.get(&para_id).cloned().unwrap_or(1))
893					.map(|_para_local_core_idx| {
894						let old_core_idx = core_idx;
895						core_idx += 1;
896						(CoreIndex(old_core_idx), ParaId::from(*para_id))
897					})
898					.collect::<Vec<_>>()
899			})
900			.collect::<Vec<_>>();
901
902		expected_cores.sort_by(|(core_a, _), (core_b, _)| core_a.0.cmp(&core_b.0));
903
904		assert_eq!(expected_cores, occupied_cores);
905
906		// We need entries in the claim queue for those:
907		all_cores.append(&mut builder.backed_in_inherent_paras.clone());
908
909		let mut core_idx = 0u32;
910		let cores = all_cores
911			.keys()
912			.flat_map(|para_id| {
913				(0..elastic_paras.get(&para_id).cloned().unwrap_or(1))
914					.map(|_para_local_core_idx| {
915						// Load an assignment into provider so that one is present to pop
916						let assignment =
917							<T as scheduler::Config>::AssignmentProvider::get_mock_assignment(
918								CoreIndex(core_idx),
919								ParaId::from(*para_id),
920							);
921
922						core_idx += 1;
923						(CoreIndex(core_idx - 1), [assignment].into())
924					})
925					.collect::<Vec<(CoreIndex, VecDeque<Assignment>)>>()
926			})
927			.collect::<BTreeMap<CoreIndex, VecDeque<Assignment>>>();
928
929		scheduler::ClaimQueue::<T>::set(cores);
930
931		Bench::<T> {
932			data: ParachainsInherentData {
933				bitfields,
934				backed_candidates,
935				disputes,
936				parent_header: Self::header(builder.block_number),
937			},
938			_session: target_session,
939			_block_number: builder.block_number,
940		}
941	}
942}