referrerpolicy=no-referrer-when-downgrade

polkadot_subsystem_bench/availability/
test_state.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::{TestAuthorities, TestConfiguration},
19	environment::GENESIS_HASH,
20	mock::runtime_api::default_node_features,
21};
22use bitvec::bitvec;
23use codec::Encode;
24use colored::Colorize;
25use itertools::Itertools;
26use polkadot_node_network_protocol::{
27	request_response::{v2::ChunkFetchingRequest, ReqProtocolNames},
28	ValidationProtocols, VersionedValidationProtocol,
29};
30use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV};
31use polkadot_node_subsystem_test_helpers::{
32	derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info,
33};
34use polkadot_node_subsystem_util::availability_chunks::availability_chunk_indices;
35use polkadot_overseer::BlockInfo;
36use polkadot_primitives::{
37	AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceiptV2 as CandidateReceipt,
38	ChunkIndex, CoreIndex, Hash, HeadData, Header, PersistedValidationData, Signed, SigningContext,
39	ValidatorIndex,
40};
41use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash};
42use sp_core::H256;
43use std::{collections::HashMap, iter::Cycle, sync::Arc};
44
45const LOG_TARGET: &str = "subsystem-bench::availability::test_state";
46
47#[derive(Clone)]
48pub struct TestState {
49	// Full test configuration
50	pub config: TestConfiguration,
51	// A cycle iterator on all PoV sizes used in the test.
52	pub pov_sizes: Cycle<std::vec::IntoIter<usize>>,
53	// Generated candidate receipts to be used in the test
54	pub candidates: Cycle<std::vec::IntoIter<CandidateReceipt>>,
55	// Map from pov size to candidate index
56	pub pov_size_to_candidate: HashMap<usize, usize>,
57	// Map from generated candidate hashes to candidate index in `available_data` and `chunks`.
58	pub candidate_hashes: HashMap<CandidateHash, usize>,
59	// Map from candidate hash to occupied core index.
60	pub candidate_hash_to_core_index: HashMap<CandidateHash, CoreIndex>,
61	// Per candidate index receipts.
62	pub candidate_receipt_templates: Vec<CandidateReceipt>,
63	// Per candidate index `AvailableData`
64	pub available_data: Vec<AvailableData>,
65	// Per candidate index chunks
66	pub chunks: Vec<Vec<ErasureChunk>>,
67	// Per-core ValidatorIndex -> ChunkIndex mapping
68	pub chunk_indices: Vec<Vec<ChunkIndex>>,
69	// Per relay chain block - candidate backed by our backing group
70	pub backed_candidates: Vec<CandidateReceipt>,
71	// Request protcol names
72	pub req_protocol_names: ReqProtocolNames,
73	// Relay chain block infos
74	pub block_infos: Vec<BlockInfo>,
75	// Chung fetching requests for backed candidates
76	pub chunk_fetching_requests: Vec<Vec<Vec<u8>>>,
77	// Pregenerated signed availability bitfields
78	pub signed_bitfields: HashMap<H256, Vec<VersionedValidationProtocol>>,
79	// Relay chain block headers
80	pub block_headers: HashMap<H256, Header>,
81	// Authority keys for the network emulation.
82	pub test_authorities: TestAuthorities,
83	// Map from generated candidate receipts
84	pub candidate_receipts: HashMap<H256, Vec<CandidateReceipt>>,
85}
86
87impl TestState {
88	pub fn new(config: &TestConfiguration) -> Self {
89		use polkadot_primitives::MutateDescriptorV2;
90		let mut test_state = Self {
91			available_data: Default::default(),
92			candidate_receipt_templates: Default::default(),
93			chunks: Default::default(),
94			pov_size_to_candidate: Default::default(),
95			pov_sizes: Vec::from(config.pov_sizes()).into_iter().cycle(),
96			candidate_hashes: HashMap::new(),
97			candidates: Vec::new().into_iter().cycle(),
98			backed_candidates: Vec::new(),
99			config: config.clone(),
100			block_infos: Default::default(),
101			chunk_fetching_requests: Default::default(),
102			signed_bitfields: Default::default(),
103			candidate_receipts: Default::default(),
104			block_headers: Default::default(),
105			test_authorities: config.generate_authorities(),
106			req_protocol_names: ReqProtocolNames::new(GENESIS_HASH, None),
107			chunk_indices: Default::default(),
108			candidate_hash_to_core_index: Default::default(),
109		};
110
111		// we use it for all candidates.
112		let persisted_validation_data = PersistedValidationData {
113			parent_head: HeadData(vec![7, 8, 9]),
114			relay_parent_number: Default::default(),
115			max_pov_size: 1024,
116			relay_parent_storage_root: Default::default(),
117		};
118
119		test_state.chunk_indices = (0..config.n_cores)
120			.map(|core_index| {
121				availability_chunk_indices(
122					&default_node_features(),
123					config.n_validators,
124					CoreIndex(core_index as u32),
125				)
126				.unwrap()
127			})
128			.collect();
129
130		// For each unique pov we create a candidate receipt.
131		for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() {
132			gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue());
133
134			let mut candidate_receipt = dummy_candidate_receipt_v2(dummy_hash());
135			let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) };
136
137			let new_available_data = AvailableData {
138				validation_data: persisted_validation_data.clone(),
139				pov: Arc::new(pov),
140			};
141
142			let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root(
143				config.n_validators,
144				&new_available_data,
145				|_, _| {},
146			);
147
148			candidate_receipt.descriptor.set_erasure_root(erasure_root);
149
150			test_state.chunks.push(new_chunks);
151			test_state.available_data.push(new_available_data);
152			test_state.pov_size_to_candidate.insert(pov_size, index);
153			test_state.candidate_receipt_templates.push(CandidateReceipt {
154				descriptor: candidate_receipt.descriptor,
155				commitments_hash: candidate_receipt.commitments_hash,
156			});
157		}
158
159		test_state.block_infos = (1..=config.num_blocks)
160			.map(|block_num| {
161				let relay_block_hash = Hash::repeat_byte(block_num as u8);
162				new_block_import_info(relay_block_hash, block_num as BlockNumber)
163			})
164			.collect();
165
166		test_state.block_headers = test_state
167			.block_infos
168			.iter()
169			.map(|info| {
170				(
171					info.hash,
172					Header {
173						digest: Default::default(),
174						number: info.number,
175						parent_hash: info.parent_hash,
176						extrinsics_root: Default::default(),
177						state_root: Default::default(),
178					},
179				)
180			})
181			.collect::<HashMap<_, _>>();
182
183		// Generate all candidates
184		let candidates_count = config.n_cores * config.num_blocks;
185		gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {candidates_count} candidates.").bright_blue());
186		test_state.candidates = (0..candidates_count)
187			.map(|index| {
188				let pov_size = test_state.pov_sizes.next().expect("This is a cycle; qed");
189				let candidate_index = *test_state
190					.pov_size_to_candidate
191					.get(&pov_size)
192					.expect("pov_size always exists; qed");
193				let mut candidate_receipt =
194					test_state.candidate_receipt_templates[candidate_index].clone();
195
196				// Make it unique.
197				candidate_receipt
198					.descriptor
199					.set_relay_parent(Hash::from_low_u64_be(index as u64));
200				// Store the new candidate in the state
201				test_state.candidate_hashes.insert(candidate_receipt.hash(), candidate_index);
202
203				let core_index = (index % config.n_cores) as u32;
204				test_state
205					.candidate_hash_to_core_index
206					.insert(candidate_receipt.hash(), core_index.into());
207
208				gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate");
209
210				candidate_receipt
211			})
212			.collect::<Vec<_>>()
213			.into_iter()
214			.cycle();
215
216		// Prepare per block candidates.
217		// Genesis block is always finalized, so we start at 1.
218		for info in test_state.block_infos.iter() {
219			for _ in 0..config.n_cores {
220				let receipt = test_state.candidates.next().expect("Cycle iterator");
221				test_state.candidate_receipts.entry(info.hash).or_default().push(receipt);
222			}
223
224			// First candidate is our backed candidate.
225			test_state.backed_candidates.push(
226				test_state
227					.candidate_receipts
228					.get(&info.hash)
229					.expect("just inserted above")
230					.first()
231					.expect("just inserted above")
232					.clone(),
233			);
234		}
235
236		test_state.chunk_fetching_requests = test_state
237			.backed_candidates
238			.iter()
239			.map(|candidate| {
240				(0..config.n_validators)
241					.map(|index| {
242						ChunkFetchingRequest {
243							candidate_hash: candidate.hash(),
244							index: ValidatorIndex(index as u32),
245						}
246						.encode()
247					})
248					.collect::<Vec<_>>()
249			})
250			.collect::<Vec<_>>();
251
252		test_state.signed_bitfields = test_state
253			.block_infos
254			.iter()
255			.map(|block_info| {
256				let signing_context =
257					SigningContext { session_index: 0, parent_hash: block_info.hash };
258				let messages = (0..config.n_validators)
259					.map(|index| {
260						let validator_public = test_state
261							.test_authorities
262							.validator_public
263							.get(index)
264							.expect("All validator keys are known");
265
266						// Node has all the chunks in the world.
267						let payload: AvailabilityBitfield =
268							AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]);
269						let signed_bitfield = Signed::<AvailabilityBitfield>::sign(
270							&test_state.test_authorities.keyring.keystore(),
271							payload,
272							&signing_context,
273							ValidatorIndex(index as u32),
274							validator_public,
275						)
276						.ok()
277						.flatten()
278						.expect("should be signed");
279
280						peer_bitfield_message_v3(block_info.hash, signed_bitfield)
281					})
282					.collect::<Vec<_>>();
283
284				(block_info.hash, messages)
285			})
286			.collect();
287
288		gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue());
289
290		test_state
291	}
292}
293
294fn peer_bitfield_message_v3(
295	relay_hash: H256,
296	signed_bitfield: Signed<AvailabilityBitfield>,
297) -> VersionedValidationProtocol {
298	let bitfield = polkadot_node_network_protocol::v3::BitfieldDistributionMessage::Bitfield(
299		relay_hash,
300		signed_bitfield.into(),
301	);
302
303	ValidationProtocols::V3(
304		polkadot_node_network_protocol::v3::ValidationProtocol::BitfieldDistribution(bitfield),
305	)
306}