mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 22:07:58 +00:00
cc1e6ac301
Adds availability-write regression tests. The results for the `availability-distribution` subsystem are volatile, so I had to reduce the precision of the test.
269 lines
8.9 KiB
Rust
269 lines
8.9 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// This file is part of Polkadot.
|
|
|
|
// Polkadot is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Polkadot is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use crate::configuration::{TestAuthorities, TestConfiguration};
|
|
use bitvec::bitvec;
|
|
use colored::Colorize;
|
|
use itertools::Itertools;
|
|
use parity_scale_codec::Encode;
|
|
use polkadot_node_network_protocol::{
|
|
request_response::v1::ChunkFetchingRequest, Versioned, VersionedValidationProtocol,
|
|
};
|
|
use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV};
|
|
use polkadot_node_subsystem_test_helpers::{
|
|
derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info,
|
|
};
|
|
use polkadot_overseer::BlockInfo;
|
|
use polkadot_primitives::{
|
|
AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, Hash, HeadData, Header,
|
|
PersistedValidationData, Signed, SigningContext, ValidatorIndex,
|
|
};
|
|
use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash};
|
|
use sp_core::H256;
|
|
use std::{collections::HashMap, iter::Cycle, sync::Arc};
|
|
|
|
const LOG_TARGET: &str = "subsystem-bench::availability::test_state";
|
|
|
|
#[derive(Clone)]
|
|
pub struct TestState {
|
|
// Full test configuration
|
|
pub config: TestConfiguration,
|
|
// A cycle iterator on all PoV sizes used in the test.
|
|
pub pov_sizes: Cycle<std::vec::IntoIter<usize>>,
|
|
// Generated candidate receipts to be used in the test
|
|
pub candidates: Cycle<std::vec::IntoIter<CandidateReceipt>>,
|
|
// Map from pov size to candidate index
|
|
pub pov_size_to_candidate: HashMap<usize, usize>,
|
|
// Map from generated candidate hashes to candidate index in `available_data` and `chunks`.
|
|
pub candidate_hashes: HashMap<CandidateHash, usize>,
|
|
// Per candidate index receipts.
|
|
pub candidate_receipt_templates: Vec<CandidateReceipt>,
|
|
// Per candidate index `AvailableData`
|
|
pub available_data: Vec<AvailableData>,
|
|
// Per candiadte index chunks
|
|
pub chunks: Vec<Vec<ErasureChunk>>,
|
|
// Per relay chain block - candidate backed by our backing group
|
|
pub backed_candidates: Vec<CandidateReceipt>,
|
|
// Relay chain block infos
|
|
pub block_infos: Vec<BlockInfo>,
|
|
// Chung fetching requests for backed candidates
|
|
pub chunk_fetching_requests: Vec<Vec<Vec<u8>>>,
|
|
// Pregenerated signed availability bitfields
|
|
pub signed_bitfields: HashMap<H256, Vec<VersionedValidationProtocol>>,
|
|
// Relay chain block headers
|
|
pub block_headers: HashMap<H256, Header>,
|
|
// Authority keys for the network emulation.
|
|
pub test_authorities: TestAuthorities,
|
|
// Map from generated candidate receipts
|
|
pub candidate_receipts: HashMap<H256, Vec<CandidateReceipt>>,
|
|
}
|
|
|
|
impl TestState {
|
|
pub fn new(config: &TestConfiguration) -> Self {
|
|
let mut test_state = Self {
|
|
available_data: Default::default(),
|
|
candidate_receipt_templates: Default::default(),
|
|
chunks: Default::default(),
|
|
pov_size_to_candidate: Default::default(),
|
|
pov_sizes: Vec::from(config.pov_sizes()).into_iter().cycle(),
|
|
candidate_hashes: HashMap::new(),
|
|
candidates: Vec::new().into_iter().cycle(),
|
|
backed_candidates: Vec::new(),
|
|
config: config.clone(),
|
|
block_infos: Default::default(),
|
|
chunk_fetching_requests: Default::default(),
|
|
signed_bitfields: Default::default(),
|
|
candidate_receipts: Default::default(),
|
|
block_headers: Default::default(),
|
|
test_authorities: config.generate_authorities(),
|
|
};
|
|
|
|
// we use it for all candidates.
|
|
let persisted_validation_data = PersistedValidationData {
|
|
parent_head: HeadData(vec![7, 8, 9]),
|
|
relay_parent_number: Default::default(),
|
|
max_pov_size: 1024,
|
|
relay_parent_storage_root: Default::default(),
|
|
};
|
|
|
|
// For each unique pov we create a candidate receipt.
|
|
for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() {
|
|
gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue());
|
|
|
|
let mut candidate_receipt = dummy_candidate_receipt(dummy_hash());
|
|
let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) };
|
|
|
|
let new_available_data = AvailableData {
|
|
validation_data: persisted_validation_data.clone(),
|
|
pov: Arc::new(pov),
|
|
};
|
|
|
|
let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root(
|
|
config.n_validators,
|
|
&new_available_data,
|
|
|_, _| {},
|
|
);
|
|
|
|
candidate_receipt.descriptor.erasure_root = erasure_root;
|
|
|
|
test_state.chunks.push(new_chunks);
|
|
test_state.available_data.push(new_available_data);
|
|
test_state.pov_size_to_candidate.insert(pov_size, index);
|
|
test_state.candidate_receipt_templates.push(candidate_receipt);
|
|
}
|
|
|
|
test_state.block_infos = (1..=config.num_blocks)
|
|
.map(|block_num| {
|
|
let relay_block_hash = Hash::repeat_byte(block_num as u8);
|
|
new_block_import_info(relay_block_hash, block_num as BlockNumber)
|
|
})
|
|
.collect();
|
|
|
|
test_state.block_headers = test_state
|
|
.block_infos
|
|
.iter()
|
|
.map(|info| {
|
|
(
|
|
info.hash,
|
|
Header {
|
|
digest: Default::default(),
|
|
number: info.number,
|
|
parent_hash: info.parent_hash,
|
|
extrinsics_root: Default::default(),
|
|
state_root: Default::default(),
|
|
},
|
|
)
|
|
})
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
// Generate all candidates
|
|
let candidates_count = config.n_cores * config.num_blocks;
|
|
gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", candidates_count).bright_blue());
|
|
test_state.candidates = (0..candidates_count)
|
|
.map(|index| {
|
|
let pov_size = test_state.pov_sizes.next().expect("This is a cycle; qed");
|
|
let candidate_index = *test_state
|
|
.pov_size_to_candidate
|
|
.get(&pov_size)
|
|
.expect("pov_size always exists; qed");
|
|
let mut candidate_receipt =
|
|
test_state.candidate_receipt_templates[candidate_index].clone();
|
|
|
|
// Make it unique.
|
|
candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64);
|
|
// Store the new candidate in the state
|
|
test_state.candidate_hashes.insert(candidate_receipt.hash(), candidate_index);
|
|
|
|
gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate");
|
|
|
|
candidate_receipt
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into_iter()
|
|
.cycle();
|
|
|
|
// Prepare per block candidates.
|
|
// Genesis block is always finalized, so we start at 1.
|
|
for info in test_state.block_infos.iter() {
|
|
for _ in 0..config.n_cores {
|
|
let receipt = test_state.candidates.next().expect("Cycle iterator");
|
|
test_state.candidate_receipts.entry(info.hash).or_default().push(receipt);
|
|
}
|
|
|
|
// First candidate is our backed candidate.
|
|
test_state.backed_candidates.push(
|
|
test_state
|
|
.candidate_receipts
|
|
.get(&info.hash)
|
|
.expect("just inserted above")
|
|
.first()
|
|
.expect("just inserted above")
|
|
.clone(),
|
|
);
|
|
}
|
|
|
|
test_state.chunk_fetching_requests = test_state
|
|
.backed_candidates
|
|
.iter()
|
|
.map(|candidate| {
|
|
(0..config.n_validators)
|
|
.map(|index| {
|
|
ChunkFetchingRequest {
|
|
candidate_hash: candidate.hash(),
|
|
index: ValidatorIndex(index as u32),
|
|
}
|
|
.encode()
|
|
})
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
test_state.signed_bitfields = test_state
|
|
.block_infos
|
|
.iter()
|
|
.map(|block_info| {
|
|
let signing_context =
|
|
SigningContext { session_index: 0, parent_hash: block_info.hash };
|
|
let messages = (0..config.n_validators)
|
|
.map(|index| {
|
|
let validator_public = test_state
|
|
.test_authorities
|
|
.validator_public
|
|
.get(index)
|
|
.expect("All validator keys are known");
|
|
|
|
// Node has all the chunks in the world.
|
|
let payload: AvailabilityBitfield =
|
|
AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]);
|
|
let signed_bitfield = Signed::<AvailabilityBitfield>::sign(
|
|
&test_state.test_authorities.keyring.keystore(),
|
|
payload,
|
|
&signing_context,
|
|
ValidatorIndex(index as u32),
|
|
validator_public,
|
|
)
|
|
.ok()
|
|
.flatten()
|
|
.expect("should be signed");
|
|
|
|
peer_bitfield_message_v2(block_info.hash, signed_bitfield)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
(block_info.hash, messages)
|
|
})
|
|
.collect();
|
|
|
|
gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue());
|
|
|
|
test_state
|
|
}
|
|
}
|
|
|
|
fn peer_bitfield_message_v2(
|
|
relay_hash: H256,
|
|
signed_bitfield: Signed<AvailabilityBitfield>,
|
|
) -> VersionedValidationProtocol {
|
|
let bitfield = polkadot_node_network_protocol::v2::BitfieldDistributionMessage::Bitfield(
|
|
relay_hash,
|
|
signed_bitfield.into(),
|
|
);
|
|
|
|
Versioned::V2(polkadot_node_network_protocol::v2::ValidationProtocol::BitfieldDistribution(
|
|
bitfield,
|
|
))
|
|
}
|