use super::*;
use crate::{
configuration::{self, HostConfiguration},
mock::MockGenesisConfig,
};
use pezkuwi_primitives::SchedulerParams;
fn default_config() -> MockGenesisConfig {
MockGenesisConfig {
configuration: configuration::GenesisConfig {
config: HostConfiguration {
max_head_data_size: 0b100000,
scheduler_params: SchedulerParams {
group_rotation_frequency: u32::MAX,
..Default::default()
},
..Default::default()
},
},
..Default::default()
}
}
#[cfg(not(feature = "runtime-benchmarks"))]
mod enter {
use super::{inclusion::tests::TestCandidateBuilder, *};
use crate::{
builder::{Bench, BenchBuilder, CandidateModifier},
disputes::clear_dispute_storage,
initializer::BufferedSessionChange,
mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, RuntimeOrigin, Test},
scheduler::common::{Assignment, AssignmentProvider},
session_info,
};
use alloc::collections::btree_map::BTreeMap;
use assert_matches::assert_matches;
use core::panic;
use pezframe_support::assert_ok;
use pezframe_system::limits;
use pezkuwi_primitives::{
ApprovedPeerId, AvailabilityBitfield, CandidateDescriptorV2, ClaimQueueOffset, CollatorId,
CollatorSignature, CommittedCandidateReceiptV2, CoreSelector, InternalVersion,
MutateDescriptorV2, UMPSignal, UncheckedSigned,
};
use pezkuwi_primitives_test_helpers::CandidateDescriptor;
use pezsp_core::ByteArray;
use pezsp_runtime::Perbill;
use pretty_assertions::assert_eq;
use rstest::rstest;
struct TestConfig {
dispute_statements: BTreeMap<u32, u32>,
dispute_sessions: Vec<u32>,
backed_and_concluding: BTreeMap<u32, u32>,
num_validators_per_core: u32,
code_upgrade: Option<u32>,
elastic_paras: BTreeMap<u32, u8>,
unavailable_cores: Vec<u32>,
v2_descriptor: bool,
approved_peer_signal: Option<ApprovedPeerId>,
candidate_modifier: Option<CandidateModifier<<Test as pezframe_system::Config>::Hash>>,
}
fn make_inherent_data(
TestConfig {
dispute_statements,
dispute_sessions,
backed_and_concluding,
num_validators_per_core,
code_upgrade,
elastic_paras,
unavailable_cores,
v2_descriptor,
candidate_modifier,
approved_peer_signal,
}: TestConfig,
) -> Bench<Test> {
let extra_cores = elastic_paras
.values()
.map(|count| *count as usize)
.sum::<usize>()
.saturating_sub(elastic_paras.len() as usize);
let total_cores = dispute_sessions.len() + backed_and_concluding.len() + extra_cores;
let mut builder = BenchBuilder::<Test>::new()
.set_max_validators((total_cores) as u32 * num_validators_per_core)
.set_elastic_paras(elastic_paras.clone())
.set_max_validators_per_core(num_validators_per_core)
.set_dispute_statements(dispute_statements)
.set_backed_and_concluding_paras(backed_and_concluding.clone())
.set_dispute_sessions(&dispute_sessions[..])
.set_unavailable_cores(unavailable_cores)
.set_candidate_descriptor_v2(v2_descriptor)
.set_candidate_modifier(candidate_modifier);
if let Some(approved_peer_signal) = approved_peer_signal {
builder = builder.set_approved_peer_signal(approved_peer_signal);
}
(0..(builder.max_cores() as usize - extra_cores)).for_each(|para_id| {
(0..elastic_paras.get(&(para_id as u32)).cloned().unwrap_or(1)).for_each(
|_para_local_core_idx| {
mock_assigner::Pezpallet::<Test>::add_test_assignment(Assignment::Bulk(
para_id.into(),
));
},
);
});
if let Some(code_size) = code_upgrade {
builder.set_code_upgrade(code_size).build()
} else {
builder.build()
}
}
fn junk_collator() -> CollatorId {
CollatorId::from_slice(&mut (0..32).into_iter().collect::<Vec<_>>().as_slice())
.expect("32 bytes; qed")
}
fn junk_collator_signature() -> CollatorSignature {
CollatorSignature::from_slice(&mut (0..64).into_iter().collect::<Vec<_>>().as_slice())
.expect("64 bytes; qed")
}
#[rstest]
#[case(true)]
#[case(false)]
fn include_backed_candidates(#[case] v2_descriptor: bool) {
let config = MockGenesisConfig::default();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let dispute_statements = BTreeMap::new();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor,
approved_peer_signal: v2_descriptor.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 2);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap(),
expected_para_inherent_data
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
2
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(0))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(0)]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(1))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(1)]
);
});
}
#[rstest]
#[case(true)]
#[case(false)]
fn include_backed_candidates_elastic_scaling(#[case] v2_descriptor: bool) {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let dispute_statements = BTreeMap::new();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 3)].into_iter().collect(),
unavailable_cores: vec![],
v2_descriptor,
approved_peer_signal: v2_descriptor.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 5);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 5);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
assert!(pezpallet::OnChainVotes::<Test>::get().is_none());
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap(),
expected_para_inherent_data
);
assert_eq!(
pezpallet::OnChainVotes::<Test>::get()
.unwrap()
.backing_validators_per_candidate
.len(),
5
);
assert_eq!(
pezpallet::OnChainVotes::<Test>::get().unwrap().session,
2
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(0))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(0)]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(1))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(1)]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(2))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(2), CoreIndex(3), CoreIndex(4)]
);
});
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let unavailable_cores = vec![0, 4, 5];
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 4)].into_iter().collect(),
unavailable_cores: unavailable_cores.clone(),
v2_descriptor,
approved_peer_signal: v2_descriptor.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let mut expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 6);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 6);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
assert!(pezpallet::OnChainVotes::<Test>::get().is_none());
expected_para_inherent_data.backed_candidates = expected_para_inherent_data
.backed_candidates
.into_iter()
.filter(|candidate| {
let (_, Some(core_index)) = candidate.validator_indices_and_core_index() else {
panic!("Core index must have been injected");
};
!unavailable_cores.contains(&core_index.0)
})
.collect();
let mut inherent_data = InherentData::new();
inherent_data.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &scenario.data).unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap(),
expected_para_inherent_data
);
assert_eq!(
pezpallet::OnChainVotes::<Test>::get()
.unwrap()
.backing_validators_per_candidate
.len(),
3
);
assert_eq!(
pezpallet::OnChainVotes::<Test>::get().unwrap().session,
2
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(1))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(1)]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(2))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![CoreIndex(4), CoreIndex(5), CoreIndex(2), CoreIndex(3)]
);
let expected_heads = (0..=2)
.map(|id| {
inclusion::PendingAvailability::<Test>::get(ParaId::from(id))
.unwrap()
.back()
.unwrap()
.candidate_commitments()
.head_data
.clone()
})
.collect::<Vec<_>>();
let mut data = scenario.data.clone();
let validators = session_info::Sessions::<Test>::get(2).unwrap().validators;
let signing_context = SigningContext {
parent_hash: BenchBuilder::<Test>::header(4).hash(),
session_index: 2,
};
data.backed_candidates.clear();
data.bitfields.iter_mut().enumerate().for_each(|(i, bitfield)| {
let unchecked_signed = UncheckedSigned::<AvailabilityBitfield>::benchmark_sign(
validators.get(ValidatorIndex(i as u32)).unwrap(),
bitvec::bitvec![u8, bitvec::order::Lsb0; 1; 6].into(),
&signing_context,
ValidatorIndex(i as u32),
);
*bitfield = unchecked_signed;
});
let mut inherent_data = InherentData::new();
inherent_data.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &data).unwrap();
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap(),
data
);
assert!(pezpallet::OnChainVotes::<Test>::get()
.unwrap()
.backing_validators_per_candidate
.is_empty());
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(0))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(1))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![]
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(2))
.unwrap()
.into_iter()
.map(|c| c.core_occupied())
.collect::<Vec<_>>(),
vec![]
);
expected_heads.into_iter().enumerate().for_each(|(id, head)| {
assert_eq!(paras::Heads::<Test>::get(ParaId::from(id as u32)).unwrap(), head);
});
});
}
#[test]
fn session_change() {
let config = MockGenesisConfig::default();
new_test_ext(config).execute_with(|| {
let dispute_statements = BTreeMap::new();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let prev_claim_queue = scheduler::ClaimQueue::<Test>::get();
assert_eq!(inclusion::PendingAvailability::<Test>::iter().count(), 2);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(0)).unwrap().len(),
1
);
assert_eq!(
inclusion::PendingAvailability::<Test>::get(ParaId::from(1)).unwrap().len(),
1
);
let mut expected_para_inherent_data = scenario.data.clone();
expected_para_inherent_data.backed_candidates.clear();
assert_eq!(expected_para_inherent_data.bitfields.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
initializer::BufferedSessionChanges::<Test>::put(vec![BufferedSessionChange {
validators: vec![],
queued: vec![],
session_index: 3,
}]);
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap(),
expected_para_inherent_data
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
0
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
assert_eq!(inclusion::PendingAvailability::<Test>::iter().count(), 2);
assert!(inclusion::PendingAvailability::<Test>::get(ParaId::from(0))
.unwrap()
.is_empty());
assert!(inclusion::PendingAvailability::<Test>::get(ParaId::from(1))
.unwrap()
.is_empty());
assert_eq!(prev_claim_queue, scheduler::ClaimQueue::<Test>::get());
});
}
#[test]
fn test_session_is_tracked_in_on_chain_scraping() {
use crate::disputes::run_to_block;
use pezkuwi_primitives::{
DisputeStatement, DisputeStatementSet, ExplicitDisputeStatement,
InvalidDisputeStatementKind, ValidDisputeStatementKind,
};
use pezsp_core::{crypto::CryptoType, Pair};
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let generate_votes = |session: u32, candidate_hash: CandidateHash| {
vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session }
.signing_payload(),
),
),
],
}]
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect::<Vec<CheckedDisputeStatementSet>>()
};
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let statements = generate_votes(3, candidate_hash);
set_scrapable_on_chain_disputes::<Test>(3, statements);
assert_matches!(pezpallet::OnChainVotes::<Test>::get(), Some(ScrapedOnChainVotes {
session,
..
} ) => {
assert_eq!(session, 3);
});
run_to_block(7, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(2));
let statements = generate_votes(7, candidate_hash);
set_scrapable_on_chain_disputes::<Test>(7, statements);
assert_matches!(pezpallet::OnChainVotes::<Test>::get(), Some(ScrapedOnChainVotes {
session,
..
} ) => {
assert_eq!(session, 7);
});
});
}
#[test]
fn filter_multi_dispute_data() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let dispute_statements = BTreeMap::new();
let backed_and_concluding = BTreeMap::new();
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![1, 2, 3 ],
backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 15);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 0);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
let multi_dispute_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(multi_dispute_inherent_data != expected_para_inherent_data);
assert_eq!(multi_dispute_inherent_data.disputes.len(), 2);
assert_eq!(
&multi_dispute_inherent_data.disputes[..2],
&expected_para_inherent_data.disputes[..2],
);
clear_dispute_storage::<Test>();
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
multi_dispute_inherent_data,
));
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
0
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
});
}
#[test]
fn limit_dispute_data() {
pezsp_tracing::try_init_simple();
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let dispute_statements = BTreeMap::new();
let backed_and_concluding = BTreeMap::new();
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 6,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 18);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 0);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert_eq!(limit_inherent_data.disputes.len(), 2);
assert_eq!(limit_inherent_data.disputes[0].session, 1);
assert_eq!(limit_inherent_data.disputes[1].session, 2);
clear_dispute_storage::<Test>();
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
limit_inherent_data,
));
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
0
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
});
}
#[test]
fn limit_dispute_data_ignore_backed_candidates() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let dispute_statements = BTreeMap::new();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 2);
backed_and_concluding.insert(1, 2);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 4,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 20);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert_eq!(limit_inherent_data.disputes.len(), 2);
assert_eq!(limit_inherent_data.disputes[0].session, 1);
assert_eq!(limit_inherent_data.disputes[1].session, 2);
assert_eq!(
limit_inherent_data.bitfields.len(),
expected_para_inherent_data.bitfields.len()
);
assert_eq!(limit_inherent_data.backed_candidates.len(), 0);
clear_dispute_storage::<Test>();
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
limit_inherent_data,
));
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
0,
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
});
}
#[test]
fn limit_bitfields_some() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let mut dispute_statements = BTreeMap::new();
dispute_statements.insert(2, 20);
dispute_statements.insert(3, 20);
dispute_statements.insert(4, 20);
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 2);
backed_and_concluding.insert(1, 2);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 25);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
assert!(!scheduler::Pezpallet::<Test>::claim_queue_is_empty());
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert_ne!(limit_inherent_data, expected_para_inherent_data);
assert!(inherent_data_weight(&limit_inherent_data)
.all_lte(inherent_data_weight(&expected_para_inherent_data)));
assert!(inherent_data_weight(&limit_inherent_data)
.all_lte(max_block_weight_proof_size_adjusted()));
assert_eq!(limit_inherent_data.disputes.len(), 2);
assert_eq!(limit_inherent_data.disputes[0].session, 1);
assert_eq!(limit_inherent_data.disputes[1].session, 2);
assert_eq!(limit_inherent_data.bitfields.len(), 20,);
assert_eq!(limit_inherent_data.backed_candidates.len(), 0);
clear_dispute_storage::<Test>();
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
limit_inherent_data
));
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
0,
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
});
}
#[test]
fn limit_bitfields_overweight() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let mut dispute_statements = BTreeMap::new();
dispute_statements.insert(2, 20);
dispute_statements.insert(3, 20);
dispute_statements.insert(4, 20);
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 2);
backed_and_concluding.insert(1, 2);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), 25);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert_eq!(limit_inherent_data.bitfields.len(), 20);
assert_eq!(limit_inherent_data.disputes.len(), 2);
assert_eq!(limit_inherent_data.backed_candidates.len(), 0);
});
}
#[test]
fn overweight_candidates_enactment_is_fine() {
pezsp_tracing::try_init_simple();
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
use crate::inclusion::WeightInfo as _;
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
let num_candidates = 5u32;
let max_weight = <Test as pezframe_system::Config>::BlockWeights::get().max_block;
assert!(<Test as inclusion::Config>::WeightInfo::enact_candidate(0, 0, 0)
.saturating_mul(u64::from(num_candidates))
.any_gt(max_weight));
for i in 0..num_candidates {
backed_and_concluding.insert(i, 2);
}
let num_validators_per_core: u32 = 5;
let num_backed = backed_and_concluding.len();
let bitfields_len = num_validators_per_core as usize * num_backed;
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![],
backed_and_concluding,
num_validators_per_core,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert_eq!(expected_para_inherent_data.bitfields.len(), bitfields_len);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), num_backed);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data == expected_para_inherent_data);
let cores = (0..num_candidates)
.into_iter()
.map(|i| {
let assignment =
<Test as scheduler::Config>::AssignmentProvider::get_mock_assignment(
CoreIndex(i),
ParaId::from(i),
);
(CoreIndex(i), [assignment].into())
})
.collect();
scheduler::ClaimQueue::<Test>::set(cores);
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
limit_inherent_data,
));
});
}
fn max_block_weight_proof_size_adjusted() -> Weight {
let raw_weight = <Test as pezframe_system::Config>::BlockWeights::get().max_block;
let block_length = <Test as pezframe_system::Config>::BlockLength::get();
raw_weight.set_proof_size(*block_length.max.get(DispatchClass::Mandatory) as u64)
}
fn inherent_data_weight(inherent_data: &TeyrchainsInherentData) -> Weight {
use thousands::Separable;
let multi_dispute_statement_sets_weight =
multi_dispute_statement_sets_weight::<Test>(&inherent_data.disputes);
let signed_bitfields_weight = signed_bitfields_weight::<Test>(&inherent_data.bitfields);
let backed_candidates_weight =
backed_candidates_weight::<Test>(&inherent_data.backed_candidates);
let sum = multi_dispute_statement_sets_weight
+ signed_bitfields_weight
+ backed_candidates_weight;
println!(
"disputes({})={} + bitfields({})={} + candidates({})={} -> {}",
inherent_data.disputes.len(),
multi_dispute_statement_sets_weight.separate_with_underscores(),
inherent_data.bitfields.len(),
signed_bitfields_weight.separate_with_underscores(),
inherent_data.backed_candidates.len(),
backed_candidates_weight.separate_with_underscores(),
sum.separate_with_underscores()
);
sum
}
#[test]
fn limit_candidates_over_weight_1() {
let config = MockGenesisConfig::default();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut dispute_statements = BTreeMap::new();
dispute_statements.insert(2, 17);
dispute_statements.insert(3, 17);
dispute_statements.insert(4, 17);
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 16);
backed_and_concluding.insert(1, 25);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert!(max_block_weight_proof_size_adjusted()
.any_lt(inherent_data_weight(&expected_para_inherent_data)));
assert_eq!(expected_para_inherent_data.bitfields.len(), 25);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert!(
max_block_weight_proof_size_adjusted()
.all_gte(inherent_data_weight(&limit_inherent_data)),
"Post limiting exceeded block weight: max={} vs. inherent={}",
max_block_weight_proof_size_adjusted(),
inherent_data_weight(&limit_inherent_data)
);
assert_eq!(limit_inherent_data.bitfields.len(), 25);
assert_eq!(limit_inherent_data.backed_candidates.len(), 1);
assert_eq!(limit_inherent_data.disputes.len(), 2);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().backing_validators_per_candidate.len(),
1
);
assert_eq!(
OnChainVotes::<Test>::get().unwrap().session,
2
);
let used_cores = 5;
let cores = (0..used_cores)
.into_iter()
.map(|i| {
let assignment =
<Test as scheduler::Config>::AssignmentProvider::get_mock_assignment(
CoreIndex(i),
ParaId::from(i),
);
(CoreIndex(i), [assignment].into())
})
.collect();
scheduler::ClaimQueue::<Test>::set(cores);
clear_dispute_storage::<Test>();
assert_ok!(Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
limit_inherent_data,
));
});
}
#[test]
fn disputes_are_size_limited() {
BlockLength::set(limits::BlockLength::max_with_normal_ratio(
600,
Perbill::from_percent(75),
));
BlockWeights::set(pezframe_system::limits::BlockWeights::simple_max(Weight::from_parts(
u64::MAX,
u64::MAX,
)));
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let mut dispute_statements = BTreeMap::new();
dispute_statements.insert(2, 7);
dispute_statements.insert(3, 7);
dispute_statements.insert(4, 7);
let backed_and_concluding = BTreeMap::new();
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert!(max_block_weight_proof_size_adjusted()
.any_lt(inherent_data_weight(&expected_para_inherent_data)));
assert_eq!(expected_para_inherent_data.bitfields.len(), 15);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 0);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert!(
max_block_weight_proof_size_adjusted()
.all_gte(inherent_data_weight(&limit_inherent_data)),
"Post limiting exceeded block weight: max={} vs. inherent={}",
max_block_weight_proof_size_adjusted(),
inherent_data_weight(&limit_inherent_data)
);
assert_eq!(limit_inherent_data.bitfields.len(), 0);
assert_eq!(limit_inherent_data.backed_candidates.len(), 0);
assert_eq!(limit_inherent_data.disputes.len(), 1);
});
}
#[test]
fn bitfields_are_size_limited() {
BlockLength::set(limits::BlockLength::max_with_normal_ratio(
600,
Perbill::from_percent(75),
));
BlockWeights::set(pezframe_system::limits::BlockWeights::simple_max(Weight::from_parts(
u64::MAX,
u64::MAX,
)));
new_test_ext(default_config()).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let dispute_statements = BTreeMap::new();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 2);
backed_and_concluding.insert(1, 2);
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: Vec::new(),
backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert!(max_block_weight_proof_size_adjusted()
.any_lt(inherent_data_weight(&expected_para_inherent_data)));
assert_eq!(expected_para_inherent_data.bitfields.len(), 10);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert!(
max_block_weight_proof_size_adjusted()
.all_gte(inherent_data_weight(&limit_inherent_data)),
"Post limiting exceeded block weight: max={} vs. inherent={}",
max_block_weight_proof_size_adjusted(),
inherent_data_weight(&limit_inherent_data)
);
assert_eq!(limit_inherent_data.bitfields.len(), 8);
assert_eq!(limit_inherent_data.backed_candidates.len(), 0);
assert_eq!(limit_inherent_data.disputes.len(), 0);
});
}
#[test]
fn candidates_are_size_limited() {
BlockLength::set(limits::BlockLength::max_with_normal_ratio(
1_300,
Perbill::from_percent(75),
));
BlockWeights::set(pezframe_system::limits::BlockWeights::simple_max(Weight::from_parts(
u64::MAX,
u64::MAX,
)));
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 2);
backed_and_concluding.insert(1, 2);
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: Vec::new(),
backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: true,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert!(max_block_weight_proof_size_adjusted()
.any_lt(inherent_data_weight(&expected_para_inherent_data)));
assert_eq!(expected_para_inherent_data.bitfields.len(), 10);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 2);
assert_eq!(expected_para_inherent_data.disputes.len(), 0);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let limit_inherent_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data.clone()).unwrap();
assert!(limit_inherent_data != expected_para_inherent_data);
assert!(
max_block_weight_proof_size_adjusted()
.all_gte(inherent_data_weight(&limit_inherent_data)),
"Post limiting exceeded block weight: max={} vs. inherent={}",
max_block_weight_proof_size_adjusted(),
inherent_data_weight(&limit_inherent_data)
);
assert_eq!(limit_inherent_data.bitfields.len(), 10);
assert_eq!(limit_inherent_data.backed_candidates.len(), 1);
assert_eq!(limit_inherent_data.disputes.len(), 0);
});
}
fn build_backed_candidate_chain(
para_id: ParaId,
len: usize,
start_core_index: usize,
code_upgrade_index: Option<usize>,
v2_receipts: bool,
) -> Vec<BackedCandidate> {
if let Some(code_upgrade_index) = code_upgrade_index {
assert!(code_upgrade_index < len, "Code upgrade index out of bounds");
}
(0..len)
.into_iter()
.map(|idx| {
let core_index = CoreIndex((start_core_index + idx) as u32);
let mut builder = TestCandidateBuilder::default();
builder.para_id = para_id;
if Some(idx) == code_upgrade_index {
builder.new_validation_code = Some(vec![1, 2, 3, 4].into());
}
if v2_receipts {
builder.core_index = Some(core_index);
builder.core_selector = Some(idx as u8);
}
let ccr = builder.build();
BackedCandidate::new(ccr.into(), Default::default(), Default::default(), core_index)
})
.collect::<Vec<_>>()
}
#[rstest]
#[case(true)]
#[case(false)]
fn test_backed_candidates_apply_weight_works_for_elastic_scaling(#[case] v2_descriptor: bool) {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let seed = [
1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3,
0, 0, 0, 0, 0, 2, 92,
];
let mut rng = rand_chacha::ChaChaRng::from_seed(seed);
let mut backed_and_concluding = BTreeMap::new();
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
v2_descriptor,
)
.unwrap();
for i in 0..30 {
backed_and_concluding.insert(i, i);
}
let scenario = make_inherent_data(TestConfig {
dispute_statements: Default::default(),
dispute_sessions: vec![],
backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor,
approved_peer_signal: v2_descriptor.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let mut para_inherent_data = scenario.data.clone();
assert_eq!(para_inherent_data.bitfields.len(), 150);
assert_eq!(para_inherent_data.backed_candidates.len(), 30);
let other_candidate_weight =
backed_candidate_weight::<Test>(¶_inherent_data.backed_candidates[0]);
let mut input_candidates =
build_backed_candidate_chain(ParaId::from(1000), 3, 0, Some(1), v2_descriptor);
let chained_candidates_weight = backed_candidates_weight::<Test>(&input_candidates);
input_candidates.append(&mut para_inherent_data.backed_candidates);
let input_bitfields = para_inherent_data.bitfields;
let max_weight =
other_candidate_weight + signed_bitfields_weight::<Test>(&input_bitfields);
let mut backed_candidates = input_candidates.clone();
let mut bitfields = input_bitfields.clone();
apply_weight_limit::<Test>(
&mut backed_candidates,
&mut bitfields,
max_weight,
&mut rng,
);
assert_eq!(backed_candidates.len(), 1);
assert_ne!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000));
assert_eq!(bitfields.len(), 150);
let max_weight =
chained_candidates_weight + signed_bitfields_weight::<Test>(&input_bitfields);
let mut backed_candidates = input_candidates.clone();
let mut bitfields = input_bitfields.clone();
apply_weight_limit::<Test>(
&mut backed_candidates,
&mut bitfields,
max_weight,
&mut rng,
);
assert_eq!(backed_candidates.len(), 3);
assert_eq!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000));
assert_eq!(backed_candidates[1].descriptor().para_id(), ParaId::from(1000));
assert_eq!(backed_candidates[2].descriptor().para_id(), ParaId::from(1000));
assert_eq!(bitfields.len(), 150);
});
}
#[test]
fn inherent_create_weight_invariant() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let mut dispute_statements = BTreeMap::new();
dispute_statements.insert(2, 100);
dispute_statements.insert(3, 200);
dispute_statements.insert(4, 300);
let mut backed_and_concluding = BTreeMap::new();
for i in 0..30 {
backed_and_concluding.insert(i, i);
}
let scenario = make_inherent_data(TestConfig {
dispute_statements,
dispute_sessions: vec![2, 2, 1], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: BTreeMap::new(),
unavailable_cores: vec![],
v2_descriptor: false,
approved_peer_signal: None,
candidate_modifier: None,
});
let expected_para_inherent_data = scenario.data.clone();
assert!(max_block_weight_proof_size_adjusted()
.any_lt(inherent_data_weight(&expected_para_inherent_data)));
assert_eq!(expected_para_inherent_data.bitfields.len(), 165);
assert_eq!(expected_para_inherent_data.backed_candidates.len(), 30);
assert_eq!(expected_para_inherent_data.disputes.len(), 3);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data)
.unwrap();
let dispatch_error = Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
expected_para_inherent_data,
)
.unwrap_err()
.error;
assert_eq!(dispatch_error, Error::<Test>::InherentDataFilteredDuringExecution.into());
});
}
#[test]
fn v2_descriptors_are_filtered() {
let config = default_config();
new_test_ext(config).execute_with(|| {
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let unavailable_cores = vec![];
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 5,
code_upgrade: None,
elastic_paras: [(2, 8)].into_iter().collect(),
unavailable_cores: unavailable_cores.clone(),
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let mut unfiltered_para_inherent_data = scenario.data.clone();
assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50);
assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10);
unfiltered_para_inherent_data.backed_candidates[9]
.descriptor_mut()
.set_version(InternalVersion(123));
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data)
.unwrap();
let filtered_para_inherend_data =
Pezpallet::<Test>::create_inherent_inner(&inherent_data).unwrap();
assert_eq!(filtered_para_inherend_data.backed_candidates.len(), 0);
let dispatch_error = Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
unfiltered_para_inherent_data,
)
.unwrap_err()
.error;
assert_eq!(dispatch_error, Error::<Test>::InherentDataFilteredDuringExecution.into());
});
}
#[test]
fn too_many_ump_signals() {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let unavailable_cores = vec![];
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 8)].into_iter().collect(),
unavailable_cores: unavailable_cores.clone(),
v2_descriptor: true,
approved_peer_signal: None,
candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| {
if candidate.descriptor.para_id() == 2.into() {
candidate.commitments.upward_messages.force_push(
UMPSignal::SelectCore(CoreSelector(123 as u8), ClaimQueueOffset(2))
.encode(),
);
}
candidate
}),
});
let unfiltered_para_inherent_data = scenario.data.clone();
assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 10);
assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data)
.unwrap();
let dispatch_error = Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
unfiltered_para_inherent_data,
)
.unwrap_err()
.error;
assert_eq!(dispatch_error, Error::<Test>::InherentDataFilteredDuringExecution.into());
});
}
#[test]
fn invalid_ump_signals() {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 8)].into_iter().collect(),
unavailable_cores: vec![],
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| {
if candidate.descriptor.para_id() == 1.into() {
candidate.commitments.upward_messages[1].truncate(0);
}
candidate
}),
});
let unfiltered_para_inherent_data = scenario.data.clone();
assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 10);
assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data)
.unwrap();
let dispatch_error = Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
unfiltered_para_inherent_data,
)
.unwrap_err()
.error;
assert_eq!(dispatch_error, Error::<Test>::InherentDataFilteredDuringExecution.into());
});
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 8)].into_iter().collect(),
unavailable_cores: vec![],
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| {
if candidate.descriptor.para_id() == 1.into() {
candidate.commitments.upward_messages[2] = vec![3u8; 70];
}
candidate
}),
});
let unfiltered_para_inherent_data = scenario.data.clone();
assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 10);
assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10);
let mut inherent_data = InherentData::new();
inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data)
.unwrap();
let dispatch_error = Pezpallet::<Test>::enter(
pezframe_system::RawOrigin::None.into(),
unfiltered_para_inherent_data,
)
.unwrap_err()
.error;
assert_eq!(dispatch_error, Error::<Test>::InherentDataFilteredDuringExecution.into());
});
}
#[rstest]
#[case(true, true)]
#[case(true, false)]
fn v2_descriptors_are_accepted(
#[case] v2_descriptor: bool,
#[case] has_approved_peer_signal: bool,
) {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let unavailable_cores = vec![];
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 3)].into_iter().collect(),
unavailable_cores: unavailable_cores.clone(),
v2_descriptor,
approved_peer_signal: has_approved_peer_signal
.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let inherent_data = scenario.data.clone();
assert_eq!(inherent_data.bitfields.len(), 5);
assert_eq!(inherent_data.backed_candidates.len(), 5);
Pezpallet::<Test>::enter(pezframe_system::RawOrigin::None.into(), inherent_data)
.unwrap();
});
}
#[test]
fn elastic_scaling_mixed_v1_v2_descriptors() {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 3)].into_iter().collect(),
unavailable_cores: vec![],
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let mut inherent_data = scenario.data.clone();
let candidate_count = inherent_data.backed_candidates.len();
for index in candidate_count - 2..candidate_count {
let encoded = inherent_data.backed_candidates[index].descriptor().encode();
let mut decoded: CandidateDescriptor =
Decode::decode(&mut encoded.as_slice()).unwrap();
decoded.collator = junk_collator();
decoded.signature = junk_collator_signature();
*inherent_data.backed_candidates[index].descriptor_mut() =
Decode::decode(&mut encoded.as_slice()).unwrap();
}
assert_eq!(inherent_data.bitfields.len(), 5);
assert_eq!(inherent_data.backed_candidates.len(), 5);
Pezpallet::<Test>::enter(pezframe_system::RawOrigin::None.into(), inherent_data)
.unwrap();
});
}
#[test]
fn mixed_v1_and_v2_optional_ump_signals() {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
backed_and_concluding.insert(3, 1);
backed_and_concluding.insert(4, 1);
let candidate_modifier = |mut candidate: CommittedCandidateReceiptV2| {
if candidate.descriptor.para_id() == ParaId::from(0) {
candidate.commitments.upward_messages.clear();
}
if candidate.descriptor.para_id() > ParaId::from(2) {
let mut v1: CandidateDescriptor = candidate.descriptor.into();
v1.collator = junk_collator();
v1.signature = junk_collator_signature();
candidate.descriptor = v1.into();
}
candidate
};
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: Default::default(),
unavailable_cores: vec![],
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: Some(candidate_modifier),
});
let inherent_data = scenario.data.clone();
assert_eq!(inherent_data.bitfields.len(), 5);
assert_eq!(inherent_data.backed_candidates.len(), 5);
let mut expected_inherent_data = inherent_data.clone();
expected_inherent_data.backed_candidates.truncate(3);
let mut create_inherent_data = InherentData::new();
create_inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &inherent_data)
.unwrap();
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&create_inherent_data).unwrap(),
expected_inherent_data
);
});
}
#[test]
fn invalid_session_index() {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let unavailable_cores = vec![];
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 3)].into_iter().collect(),
unavailable_cores,
v2_descriptor: true,
approved_peer_signal: Some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: None,
});
let mut inherent_data = scenario.data.clone();
assert_eq!(inherent_data.bitfields.len(), 5);
assert_eq!(inherent_data.backed_candidates.len(), 5);
let index = inherent_data.backed_candidates.len() - 1;
let backed_candidate = inherent_data.backed_candidates[index].clone();
let candidate = CommittedCandidateReceiptV2 {
descriptor: CandidateDescriptorV2::new(
backed_candidate.descriptor().para_id(),
backed_candidate.descriptor().relay_parent(),
backed_candidate.descriptor().core_index().unwrap(),
100,
backed_candidate.descriptor().persisted_validation_data_hash(),
backed_candidate.descriptor().pov_hash(),
backed_candidate.descriptor().erasure_root(),
backed_candidate.descriptor().para_head(),
backed_candidate.descriptor().validation_code_hash(),
),
commitments: backed_candidate.candidate().commitments.clone(),
};
let (validator_indices, core_index) =
backed_candidate.validator_indices_and_core_index();
inherent_data.backed_candidates[index] = BackedCandidate::new(
candidate,
backed_candidate.validity_votes().to_vec(),
validator_indices.into(),
core_index.unwrap(),
);
let mut expected_inherent_data = inherent_data.clone();
expected_inherent_data.backed_candidates.truncate(index);
let mut create_inherent_data = InherentData::new();
create_inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &inherent_data)
.unwrap();
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&create_inherent_data).unwrap(),
expected_inherent_data
);
Pezpallet::<Test>::enter(pezframe_system::RawOrigin::None.into(), inherent_data)
.unwrap_err();
});
}
#[rstest]
#[case(true)]
#[case(false)]
fn candidate_without_core_index(#[case] v2_descriptor: bool) {
let config = default_config();
new_test_ext(config).execute_with(|| {
configuration::Pezpallet::<Test>::set_node_feature(
RuntimeOrigin::root(),
FeatureIndex::CandidateReceiptV2 as u8,
true,
)
.unwrap();
let mut backed_and_concluding = BTreeMap::new();
backed_and_concluding.insert(0, 1);
backed_and_concluding.insert(1, 1);
backed_and_concluding.insert(2, 1);
let scenario = make_inherent_data(TestConfig {
dispute_statements: BTreeMap::new(),
dispute_sessions: vec![], backed_and_concluding,
num_validators_per_core: 1,
code_upgrade: None,
elastic_paras: [(2, 3)].into_iter().collect(),
unavailable_cores: vec![],
v2_descriptor,
approved_peer_signal: v2_descriptor.then_some(vec![1, 2, 3].try_into().unwrap()),
candidate_modifier: Some(|mut candidate| {
if candidate.descriptor.para_id() == ParaId::from(0) {
candidate.commitments.upward_messages.clear();
let mut v1: CandidateDescriptor = candidate.descriptor.into();
v1.collator = junk_collator();
v1.signature = junk_collator_signature();
candidate.descriptor = v1.into();
}
candidate
}),
});
let mut inherent_data = scenario.data.clone();
let (validator_indices, _) =
inherent_data.backed_candidates[0].validator_indices_and_core_index();
let validator_indices = validator_indices.into();
inherent_data.backed_candidates[0]
.set_validator_indices_and_core_index(validator_indices, None);
assert_eq!(inherent_data.bitfields.len(), 5);
assert_eq!(inherent_data.backed_candidates.len(), 5);
let mut create_inherent_data = InherentData::new();
create_inherent_data
.put_data(TEYRCHAINS_INHERENT_IDENTIFIER, &inherent_data)
.unwrap();
let mut expected_inherent_data = inherent_data.clone();
expected_inherent_data.backed_candidates.remove(0);
assert_eq!(
Pezpallet::<Test>::create_inherent_inner(&create_inherent_data).unwrap(),
expected_inherent_data
);
assert_eq!(
Pezpallet::<Test>::enter(pezframe_system::RawOrigin::None.into(), inherent_data)
.unwrap_err()
.error,
Error::<Test>::InherentDataFilteredDuringExecution.into()
);
});
}
}
fn default_header() -> pezkuwi_primitives::Header {
pezkuwi_primitives::Header {
parent_hash: Default::default(),
number: 0,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
}
}
mod sanitizers {
use super::*;
use crate::{
inclusion::tests::{back_candidate, BackingKind, TestCandidateBuilder},
mock::new_test_ext,
};
use bitvec::order::Lsb0;
use pezkuwi_primitives::{
AvailabilityBitfield, GroupIndex, Hash, Id as ParaId, SignedAvailabilityBitfield,
ValidatorIndex,
};
use pezsp_core::crypto::UncheckedFrom;
use rstest::rstest;
use crate::mock::Test;
use pezkuwi_primitives::TEYRCHAIN_KEY_TYPE_ID;
use pezsc_keystore::LocalKeystore;
use pezsp_keystore::{Keystore, KeystorePtr};
use std::sync::Arc;
fn validator_pubkeys(val_ids: &[pezsp_keyring::Sr25519Keyring]) -> Vec<ValidatorId> {
val_ids.iter().map(|v| v.public().into()).collect()
}
#[test]
fn bitfields() {
let header = default_header();
let parent_hash = header.hash();
let expected_bits = 2;
let session_index = SessionIndex::from(0_u32);
let crypto_store = LocalKeystore::in_memory();
let crypto_store = Arc::new(crypto_store) as KeystorePtr;
let signing_context = SigningContext { parent_hash, session_index };
let validators = vec![
pezsp_keyring::Sr25519Keyring::Alice,
pezsp_keyring::Sr25519Keyring::Bob,
pezsp_keyring::Sr25519Keyring::Charlie,
pezsp_keyring::Sr25519Keyring::Dave,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*crypto_store,
TEYRCHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let validator_public = validator_pubkeys(&validators);
let checked_bitfields = [
BitVec::<u8, Lsb0>::repeat(true, expected_bits),
BitVec::<u8, Lsb0>::repeat(true, expected_bits),
{
let mut bv = BitVec::<u8, Lsb0>::repeat(false, expected_bits);
bv.set(expected_bits - 1, true);
bv
},
]
.iter()
.enumerate()
.map(|(vi, ab)| {
let validator_index = ValidatorIndex::from(vi as u32);
SignedAvailabilityBitfield::sign(
&crypto_store,
AvailabilityBitfield::from(ab.clone()),
&signing_context,
validator_index,
&validator_public[vi],
)
.unwrap()
.unwrap()
})
.collect::<Vec<SignedAvailabilityBitfield>>();
let unchecked_bitfields = checked_bitfields
.iter()
.cloned()
.map(|v| v.into_unchecked())
.collect::<Vec<_>>();
let disputed_bitfield = DisputedBitfield::zeros(expected_bits);
{
assert_eq!(
sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
),
checked_bitfields.clone()
);
assert_eq!(
sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
),
checked_bitfields.clone()
);
}
{
let mut disputed_bitfield = DisputedBitfield::zeros(expected_bits);
disputed_bitfield.0.set(0, true);
assert_eq!(
sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
)
.len(),
1
);
assert_eq!(
sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
)
.len(),
1
);
}
{
assert!(sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits + 1,
parent_hash,
session_index,
&validator_public[..],
)
.is_empty());
assert!(sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits + 1,
parent_hash,
session_index,
&validator_public[..],
)
.is_empty());
}
{
let shortened = validator_public.len() - 2;
assert_eq!(
&sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..shortened],
)[..],
&checked_bitfields[..shortened]
);
assert_eq!(
&sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..shortened],
)[..],
&checked_bitfields[..shortened]
);
}
{
let mut unchecked_bitfields = unchecked_bitfields.clone();
let x = unchecked_bitfields.swap_remove(0);
unchecked_bitfields.push(x);
let result: UncheckedSignedAvailabilityBitfields = sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
)
.into_iter()
.map(|v| v.into_unchecked())
.collect();
assert_eq!(&result, &unchecked_bitfields[..(unchecked_bitfields.len() - 2)]);
}
{
let mut unchecked_bitfields = unchecked_bitfields.clone();
let last_bit_idx = unchecked_bitfields.len() - 1;
unchecked_bitfields
.get_mut(last_bit_idx)
.and_then(|u| Some(u.set_signature(UncheckedFrom::unchecked_from([1u8; 64]))))
.expect("we are accessing a valid index");
assert_eq!(
&sanitize_bitfields::<Test>(
unchecked_bitfields.clone(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
)[..],
&checked_bitfields[..last_bit_idx]
);
}
{
let mut unchecked_bitfields = unchecked_bitfields.clone();
let last_bit_idx = unchecked_bitfields.len() - 1;
unchecked_bitfields
.get_mut(last_bit_idx)
.and_then(|u| Some(u.set_signature(UncheckedFrom::unchecked_from([1u8; 64]))))
.expect("we are accessing a valid index");
assert_eq!(
&sanitize_bitfields::<Test>(
unchecked_bitfields.clone().into_iter().chain(unchecked_bitfields).collect(),
disputed_bitfield.clone(),
expected_bits,
parent_hash,
session_index,
&validator_public[..],
)[..],
&checked_bitfields[..last_bit_idx]
);
}
}
mod candidates {
use crate::{
mock::{set_disabled_validators, RuntimeOrigin},
scheduler::common::Assignment,
util::{make_persisted_validation_data, make_persisted_validation_data_with_parent},
};
use alloc::collections::vec_deque::VecDeque;
use pezkuwi_primitives::ValidationCode;
use super::*;
struct TestData {
backed_candidates: Vec<BackedCandidate>,
expected_backed_candidates_with_core:
BTreeMap<ParaId, Vec<(BackedCandidate, CoreIndex)>>,
scheduled_paras: BTreeMap<pezkuwi_primitives::Id, BTreeSet<CoreIndex>>,
}
fn get_test_data_one_core_per_para(backing_kind: BackingKind) -> TestData {
const RELAY_PARENT_NUM: u32 = 3;
shared::Pezpallet::<Test>::add_allowed_relay_parent(
default_header().hash(),
Default::default(),
Default::default(),
RELAY_PARENT_NUM,
1,
);
let header = default_header();
let relay_parent = header.hash();
let session_index = SessionIndex::from(0_u32);
let keystore = LocalKeystore::in_memory();
let keystore = Arc::new(keystore) as KeystorePtr;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
pezsp_keyring::Sr25519Keyring::Alice,
pezsp_keyring::Sr25519Keyring::Bob,
pezsp_keyring::Sr25519Keyring::Charlie,
pezsp_keyring::Sr25519Keyring::Dave,
pezsp_keyring::Sr25519Keyring::Eve,
pezsp_keyring::Sr25519Keyring::Ferdie,
pezsp_keyring::Sr25519Keyring::One,
pezsp_keyring::Sr25519Keyring::Two,
pezsp_keyring::Sr25519Keyring::AliceStash,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
TEYRCHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let validator_ids =
validators.iter().map(|v| v.public().into()).collect::<Vec<ValidatorId>>();
shared::Pezpallet::<Test>::set_active_validators_ascending(validator_ids);
let scheduled: BTreeMap<ParaId, BTreeSet<CoreIndex>> = (0_usize..2)
.into_iter()
.map(|idx| {
(
ParaId::from(1_u32 + idx as u32),
[CoreIndex::from(idx as u32)].into_iter().collect(),
)
})
.collect::<BTreeMap<_, _>>();
scheduler::Pezpallet::<Test>::set_validator_groups(vec![
vec![ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3)],
vec![ValidatorIndex(4), ValidatorIndex(5), ValidatorIndex(6), ValidatorIndex(7)],
]);
scheduler::Pezpallet::<Test>::set_claim_queue(BTreeMap::from([
(
CoreIndex::from(0),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(0),
}]),
),
(
CoreIndex::from(1),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(1),
}]),
),
]));
paras::Pezpallet::<Test>::set_current_head(ParaId::from(1), HeadData(vec![1]));
paras::Pezpallet::<Test>::set_current_head(ParaId::from(2), HeadData(vec![2]));
paras::Pezpallet::<Test>::force_set_current_code(
RuntimeOrigin::root(),
ParaId::from(1),
ValidationCode(vec![1]),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_current_code(
RuntimeOrigin::root(),
ParaId::from(2),
ValidationCode(vec![2]),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(1),
BlockNumberFor::<Test>::from(0u32),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(2),
BlockNumberFor::<Test>::from(0u32),
)
.unwrap();
let group_validators = |group_index: GroupIndex| {
match group_index {
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1, 2, 3]),
group_index if group_index == GroupIndex::from(1) => Some(vec![4, 5, 6, 7]),
_ => panic!("Group index out of bounds"),
}
.map(|m| m.into_iter().map(ValidatorIndex).collect::<Vec<_>>())
};
let backed_candidates = (0_usize..2)
.into_iter()
.map(|idx0| {
let idx1 = idx0 + 1;
let candidate = TestCandidateBuilder {
para_id: ParaId::from(idx1),
relay_parent,
pov_hash: Hash::repeat_byte(idx1 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(idx1),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![idx1 as u8]),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(idx0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
backing_kind,
CoreIndex(idx0 as u32),
);
backed
})
.collect::<Vec<_>>();
assert_eq!(
Pezpallet::<Test>::eligible_paras(&Default::default()).collect::<Vec<_>>(),
vec![(CoreIndex(0), ParaId::from(1)), (CoreIndex(1), ParaId::from(2))]
);
assert_eq!(
shared::ActiveValidatorIndices::<Test>::get(),
vec![
ValidatorIndex(0),
ValidatorIndex(1),
ValidatorIndex(2),
ValidatorIndex(3),
ValidatorIndex(4),
ValidatorIndex(5),
ValidatorIndex(6),
ValidatorIndex(7),
ValidatorIndex(8),
]
);
let mut expected_backed_candidates_with_core = BTreeMap::new();
for candidate in backed_candidates.iter() {
let para_id = candidate.descriptor().para_id();
expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push((
candidate.clone(),
scheduled.get(¶_id).unwrap().first().copied().unwrap(),
));
}
TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
}
}
fn get_test_data_multiple_cores_per_para(v2_descriptor: bool) -> TestData {
const RELAY_PARENT_NUM: u32 = 3;
let header = default_header();
let relay_parent = header.hash();
let session_index = SessionIndex::from(0_u32);
let keystore = LocalKeystore::in_memory();
let keystore = Arc::new(keystore) as KeystorePtr;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
pezsp_keyring::Sr25519Keyring::Alice,
pezsp_keyring::Sr25519Keyring::Bob,
pezsp_keyring::Sr25519Keyring::Charlie,
pezsp_keyring::Sr25519Keyring::Dave,
pezsp_keyring::Sr25519Keyring::Eve,
pezsp_keyring::Sr25519Keyring::Ferdie,
pezsp_keyring::Sr25519Keyring::One,
pezsp_keyring::Sr25519Keyring::Two,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
TEYRCHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let validator_ids =
validators.iter().map(|v| v.public().into()).collect::<Vec<ValidatorId>>();
shared::Pezpallet::<Test>::set_active_validators_ascending(validator_ids);
scheduler::Pezpallet::<Test>::set_validator_groups(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3)],
vec![ValidatorIndex(4)],
vec![ValidatorIndex(5)],
vec![ValidatorIndex(6)],
vec![ValidatorIndex(7)],
]);
scheduler::Pezpallet::<Test>::set_claim_queue(BTreeMap::from([
(
CoreIndex::from(0),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(0),
}]),
),
(
CoreIndex::from(1),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(1),
}]),
),
(
CoreIndex::from(2),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(2),
}]),
),
(
CoreIndex::from(3),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(3),
}]),
),
(
CoreIndex::from(4),
VecDeque::from([Assignment::Pool {
para_id: 3.into(),
core_index: CoreIndex(4),
}]),
),
(
CoreIndex::from(5),
VecDeque::from([Assignment::Pool {
para_id: 4.into(),
core_index: CoreIndex(5),
}]),
),
(
CoreIndex::from(6),
VecDeque::from([Assignment::Pool {
para_id: 5.into(),
core_index: CoreIndex(6),
}]),
),
(
CoreIndex::from(7),
VecDeque::from([Assignment::Pool {
para_id: 7.into(),
core_index: CoreIndex(7),
}]),
),
(
CoreIndex::from(8),
VecDeque::from([Assignment::Pool {
para_id: 7.into(),
core_index: CoreIndex(8),
}]),
),
(
CoreIndex::from(9),
VecDeque::from([Assignment::Pool {
para_id: 8.into(),
core_index: CoreIndex(9),
}]),
),
]));
shared::Pezpallet::<Test>::add_allowed_relay_parent(
relay_parent,
Default::default(),
scheduler::ClaimQueue::<Test>::get()
.into_iter()
.map(|(core_index, paras)| {
(core_index, paras.into_iter().map(|e| e.para_id()).collect())
})
.collect(),
RELAY_PARENT_NUM,
1,
);
for id in 1..=8u32 {
paras::Pezpallet::<Test>::set_current_head(
ParaId::from(id),
HeadData(vec![id as u8]),
);
paras::Pezpallet::<Test>::force_set_current_code(
RuntimeOrigin::root(),
ParaId::from(id),
ValidationCode(vec![id as u8]),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(id),
BlockNumberFor::<Test>::from(0u32),
)
.unwrap();
}
let group_validators = |group_index: GroupIndex| {
if group_index.0 as usize >= validators.len() {
panic!("Group index out of bounds")
} else {
Some(vec![ValidatorIndex(group_index.0)])
}
};
let mut backed_candidates = vec![];
let mut expected_backed_candidates_with_core = BTreeMap::new();
let maybe_core_index = |core_index: CoreIndex| -> Option<CoreIndex> {
if !v2_descriptor {
None
} else {
Some(core_index)
}
};
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(1 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(1),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
head_data: HeadData(vec![1, 1]),
validation_code: ValidationCode(vec![1]),
core_index: maybe_core_index(CoreIndex(0)),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(0 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(1))
.or_insert(vec![])
.push((backed, CoreIndex(0)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(2 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![1]),
core_index: maybe_core_index(CoreIndex(1)),
core_selector: Some(1),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(1 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(1))
.or_insert(vec![])
.push((backed, CoreIndex(1)));
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(3 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(2),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
core_index: maybe_core_index(CoreIndex(2)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(2 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(2))
.or_insert(vec![])
.push((backed, CoreIndex(2)));
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(3),
relay_parent,
pov_hash: Hash::repeat_byte(4 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(3),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![3]),
core_index: maybe_core_index(CoreIndex(4)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(4 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(3))
.or_insert(vec![])
.push((backed, CoreIndex(4)));
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(4),
relay_parent,
pov_hash: Hash::repeat_byte(5 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(4),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![4]),
core_index: maybe_core_index(CoreIndex(5)),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(5 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(4))
.or_insert(vec![])
.push((backed, CoreIndex(5)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(4),
relay_parent,
pov_hash: Hash::repeat_byte(6 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![4]),
core_index: maybe_core_index(CoreIndex(5)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(5 as u32),
);
backed_candidates.push(backed.clone());
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(6),
relay_parent,
pov_hash: Hash::repeat_byte(3 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(6),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![6]),
core_index: maybe_core_index(CoreIndex(6)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(6 as u32),
);
backed_candidates.push(backed.clone());
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(7),
relay_parent,
pov_hash: Hash::repeat_byte(3 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(7),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![7]),
core_index: maybe_core_index(CoreIndex(6)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(6 as u32),
);
backed_candidates.push(backed.clone());
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(8),
relay_parent,
pov_hash: Hash::repeat_byte(3 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(8),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![8]),
core_index: maybe_core_index(CoreIndex(7)),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(7 as u32),
);
backed_candidates.push(backed.clone());
}
assert_eq!(
Pezpallet::<Test>::eligible_paras(&Default::default()).collect::<Vec<_>>(),
vec![
(CoreIndex(0), ParaId::from(1)),
(CoreIndex(1), ParaId::from(1)),
(CoreIndex(2), ParaId::from(2)),
(CoreIndex(3), ParaId::from(2)),
(CoreIndex(4), ParaId::from(3)),
(CoreIndex(5), ParaId::from(4)),
(CoreIndex(6), ParaId::from(5)),
(CoreIndex(7), ParaId::from(7)),
(CoreIndex(8), ParaId::from(7)),
(CoreIndex(9), ParaId::from(8)),
]
);
let mut scheduled: BTreeMap<ParaId, BTreeSet<CoreIndex>> = BTreeMap::new();
for (core_idx, para_id) in Pezpallet::<Test>::eligible_paras(&Default::default()) {
scheduled.entry(para_id).or_default().insert(core_idx);
}
assert_eq!(
shared::ActiveValidatorIndices::<Test>::get(),
vec![
ValidatorIndex(0),
ValidatorIndex(1),
ValidatorIndex(2),
ValidatorIndex(3),
ValidatorIndex(4),
ValidatorIndex(5),
ValidatorIndex(6),
ValidatorIndex(7),
]
);
TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
}
}
fn get_test_data_for_order_checks() -> TestData {
const RELAY_PARENT_NUM: u32 = 3;
let header = default_header();
let relay_parent = header.hash();
let session_index = SessionIndex::from(0_u32);
let keystore = LocalKeystore::in_memory();
let keystore = Arc::new(keystore) as KeystorePtr;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
pezsp_keyring::Sr25519Keyring::Alice,
pezsp_keyring::Sr25519Keyring::Bob,
pezsp_keyring::Sr25519Keyring::Charlie,
pezsp_keyring::Sr25519Keyring::Dave,
pezsp_keyring::Sr25519Keyring::Eve,
pezsp_keyring::Sr25519Keyring::Ferdie,
pezsp_keyring::Sr25519Keyring::One,
pezsp_keyring::Sr25519Keyring::Two,
pezsp_keyring::Sr25519Keyring::AliceStash,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
TEYRCHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let validator_ids =
validators.iter().map(|v| v.public().into()).collect::<Vec<ValidatorId>>();
shared::Pezpallet::<Test>::set_active_validators_ascending(validator_ids);
scheduler::Pezpallet::<Test>::set_validator_groups(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3)],
vec![ValidatorIndex(4)],
vec![ValidatorIndex(5)],
vec![ValidatorIndex(6)],
vec![ValidatorIndex(7)],
vec![ValidatorIndex(8)],
]);
scheduler::Pezpallet::<Test>::set_claim_queue(BTreeMap::from([
(
CoreIndex::from(0),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(0),
}]),
),
(
CoreIndex::from(1),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(1),
}]),
),
(
CoreIndex::from(2),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(2),
}]),
),
(
CoreIndex::from(3),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(3),
}]),
),
(
CoreIndex::from(4),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(4),
}]),
),
(
CoreIndex::from(5),
VecDeque::from([Assignment::Pool {
para_id: 3.into(),
core_index: CoreIndex(5),
}]),
),
(
CoreIndex::from(6),
VecDeque::from([Assignment::Pool {
para_id: 3.into(),
core_index: CoreIndex(6),
}]),
),
(
CoreIndex::from(7),
VecDeque::from([Assignment::Pool {
para_id: 4.into(),
core_index: CoreIndex(7),
}]),
),
(
CoreIndex::from(8),
VecDeque::from([Assignment::Pool {
para_id: 4.into(),
core_index: CoreIndex(8),
}]),
),
]));
shared::Pezpallet::<Test>::add_allowed_relay_parent(
relay_parent,
Default::default(),
scheduler::ClaimQueue::<Test>::get()
.into_iter()
.map(|(core_index, paras)| {
(core_index, paras.into_iter().map(|e| e.para_id()).collect())
})
.collect(),
RELAY_PARENT_NUM,
1,
);
for id in 1..=4u32 {
paras::Pezpallet::<Test>::set_current_head(
ParaId::from(id),
HeadData(vec![id as u8]),
);
paras::Pezpallet::<Test>::force_set_current_code(
RuntimeOrigin::root(),
ParaId::from(id),
ValidationCode(vec![id as u8]),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(id),
BlockNumberFor::<Test>::from(0u32),
)
.unwrap();
}
let group_validators = |group_index: GroupIndex| {
if group_index.0 as usize >= validators.len() {
panic!("Group index out of bounds")
} else {
Some(vec![ValidatorIndex(group_index.0)])
}
};
let mut backed_candidates = vec![];
let mut expected_backed_candidates_with_core = BTreeMap::new();
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(1 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(1),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![1, 1]),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![1]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let prev_backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(0 as u32),
);
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(2 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![1]),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(1 as u32),
);
backed_candidates.push(backed.clone());
backed_candidates.push(prev_backed.clone());
}
{
let candidate_1 = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(3 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(2),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![2, 2]),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
..Default::default()
}
.build();
let backed_1: BackedCandidate = back_candidate(
candidate_1,
&validators,
group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(2 as u32),
);
backed_candidates.push(backed_1.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(2))
.or_insert(vec![])
.push((backed_1, CoreIndex(2)));
let candidate_2 = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(4 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(2),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
head_data: HeadData(vec![3, 3]),
..Default::default()
}
.build();
let backed_2 = back_candidate(
candidate_2.clone(),
&validators,
group_validators(GroupIndex::from(3 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(3 as u32),
);
backed_candidates.push(backed_2.clone());
let candidate_3 = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(5 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
candidate_2.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
..Default::default()
}
.build();
let backed_3 = back_candidate(
candidate_3,
&validators,
group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(4 as u32),
);
backed_candidates.push(backed_3.clone());
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(3),
relay_parent,
pov_hash: Hash::repeat_byte(6 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(3),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![3, 3]),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![3]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(5 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(3))
.or_insert(vec![])
.push((backed, CoreIndex(5)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(3),
relay_parent,
pov_hash: Hash::repeat_byte(6 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![3]),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(6 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(3))
.or_insert(vec![])
.push((backed, CoreIndex(6)));
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(4),
relay_parent,
pov_hash: Hash::repeat_byte(8 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(4),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![4]),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![4]),
..Default::default()
}
.build();
let backed: BackedCandidate = back_candidate(
candidate.clone(),
&validators,
group_validators(GroupIndex::from(7 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(7 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(4))
.or_insert(vec![])
.push((backed, CoreIndex(7)));
let backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(7 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(8 as u32),
);
backed_candidates.push(backed.clone());
}
assert_eq!(
Pezpallet::<Test>::eligible_paras(&Default::default()).collect::<Vec<_>>(),
vec![
(CoreIndex(0), ParaId::from(1)),
(CoreIndex(1), ParaId::from(1)),
(CoreIndex(2), ParaId::from(2)),
(CoreIndex(3), ParaId::from(2)),
(CoreIndex(4), ParaId::from(2)),
(CoreIndex(5), ParaId::from(3)),
(CoreIndex(6), ParaId::from(3)),
(CoreIndex(7), ParaId::from(4)),
(CoreIndex(8), ParaId::from(4)),
]
);
let mut scheduled: BTreeMap<ParaId, BTreeSet<CoreIndex>> = BTreeMap::new();
for (core_idx, para_id) in Pezpallet::<Test>::eligible_paras(&Default::default()) {
scheduled.entry(para_id).or_default().insert(core_idx);
}
assert_eq!(
shared::ActiveValidatorIndices::<Test>::get(),
vec![
ValidatorIndex(0),
ValidatorIndex(1),
ValidatorIndex(2),
ValidatorIndex(3),
ValidatorIndex(4),
ValidatorIndex(5),
ValidatorIndex(6),
ValidatorIndex(7),
ValidatorIndex(8),
]
);
TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
}
}
fn get_test_data_for_relay_parent_ordering() -> TestData {
const RELAY_PARENT_NUM: u32 = 3;
let header = default_header();
let relay_parent = header.hash();
let prev_relay_parent = pezkuwi_primitives::Header {
parent_hash: Default::default(),
number: RELAY_PARENT_NUM - 1,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
}
.hash();
let next_relay_parent = pezkuwi_primitives::Header {
parent_hash: Default::default(),
number: RELAY_PARENT_NUM + 1,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
}
.hash();
shared::Pezpallet::<Test>::add_allowed_relay_parent(
prev_relay_parent,
Default::default(),
Default::default(),
RELAY_PARENT_NUM - 1,
2,
);
shared::Pezpallet::<Test>::add_allowed_relay_parent(
relay_parent,
Default::default(),
Default::default(),
RELAY_PARENT_NUM,
2,
);
shared::Pezpallet::<Test>::add_allowed_relay_parent(
next_relay_parent,
Default::default(),
Default::default(),
RELAY_PARENT_NUM + 1,
2,
);
let session_index = SessionIndex::from(0_u32);
let keystore = LocalKeystore::in_memory();
let keystore = Arc::new(keystore) as KeystorePtr;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
pezsp_keyring::Sr25519Keyring::Alice,
pezsp_keyring::Sr25519Keyring::Bob,
pezsp_keyring::Sr25519Keyring::Charlie,
pezsp_keyring::Sr25519Keyring::Dave,
pezsp_keyring::Sr25519Keyring::Eve,
pezsp_keyring::Sr25519Keyring::Ferdie,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
TEYRCHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let validator_ids =
validators.iter().map(|v| v.public().into()).collect::<Vec<ValidatorId>>();
shared::Pezpallet::<Test>::set_active_validators_ascending(validator_ids);
scheduler::Pezpallet::<Test>::set_validator_groups(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3)],
vec![ValidatorIndex(4)],
vec![ValidatorIndex(5)],
]);
scheduler::Pezpallet::<Test>::set_claim_queue(BTreeMap::from([
(
CoreIndex::from(0),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(0),
}]),
),
(
CoreIndex::from(1),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(1),
}]),
),
(
CoreIndex::from(2),
VecDeque::from([Assignment::Pool {
para_id: 1.into(),
core_index: CoreIndex(2),
}]),
),
(
CoreIndex::from(3),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(3),
}]),
),
(
CoreIndex::from(4),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(4),
}]),
),
(
CoreIndex::from(5),
VecDeque::from([Assignment::Pool {
para_id: 2.into(),
core_index: CoreIndex(5),
}]),
),
]));
for id in 1..=2u32 {
paras::Pezpallet::<Test>::set_current_head(
ParaId::from(id),
HeadData(vec![id as u8]),
);
paras::Pezpallet::<Test>::force_set_current_code(
RuntimeOrigin::root(),
ParaId::from(id),
ValidationCode(vec![id as u8]),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(id),
BlockNumberFor::<Test>::from(0u32),
)
.unwrap();
}
let group_validators = |group_index: GroupIndex| {
if group_index.0 as usize >= validators.len() {
panic!("Group index out of bounds")
} else {
Some(vec![ValidatorIndex(group_index.0)])
}
};
let mut backed_candidates = vec![];
let mut expected_backed_candidates_with_core = BTreeMap::new();
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(1 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(1),
RELAY_PARENT_NUM,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![1, 1]),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![1]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(0 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(1))
.or_insert(vec![])
.push((backed, CoreIndex(0)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent: prev_relay_parent,
pov_hash: Hash::repeat_byte(1 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM - 1,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM - 1,
validation_code: ValidationCode(vec![1]),
head_data: HeadData(vec![1, 1, 1]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(1 as u32),
);
backed_candidates.push(backed.clone());
let candidate = TestCandidateBuilder {
para_id: ParaId::from(1),
relay_parent,
pov_hash: Hash::repeat_byte(1 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![1]),
head_data: HeadData(vec![1, 1, 1, 1]),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(2 as u32),
);
backed_candidates.push(backed.clone());
}
{
let candidate = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent: prev_relay_parent,
pov_hash: Hash::repeat_byte(2 as u8),
persisted_validation_data_hash: make_persisted_validation_data::<Test>(
ParaId::from(2),
RELAY_PARENT_NUM - 1,
Default::default(),
)
.unwrap()
.hash(),
head_data: HeadData(vec![2, 2]),
hrmp_watermark: RELAY_PARENT_NUM - 1,
validation_code: ValidationCode(vec![2]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed: BackedCandidate = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(3 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(3 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(2))
.or_insert(vec![])
.push((backed, CoreIndex(3)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(2 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
head_data: HeadData(vec![2, 2, 2]),
..Default::default()
}
.build();
let prev_candidate = candidate.clone();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(4 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(2))
.or_insert(vec![])
.push((backed, CoreIndex(4)));
let candidate = TestCandidateBuilder {
para_id: ParaId::from(2),
relay_parent,
pov_hash: Hash::repeat_byte(2 as u8),
persisted_validation_data_hash: make_persisted_validation_data_with_parent::<
Test,
>(
RELAY_PARENT_NUM,
Default::default(),
prev_candidate.commitments.head_data,
)
.hash(),
hrmp_watermark: RELAY_PARENT_NUM,
validation_code: ValidationCode(vec![2]),
head_data: HeadData(vec![2, 2, 2, 2]),
..Default::default()
}
.build();
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
CoreIndex(5 as u32),
);
backed_candidates.push(backed.clone());
expected_backed_candidates_with_core
.entry(ParaId::from(2))
.or_insert(vec![])
.push((backed, CoreIndex(5)));
}
assert_eq!(
Pezpallet::<Test>::eligible_paras(&Default::default()).collect::<Vec<_>>(),
vec![
(CoreIndex(0), ParaId::from(1)),
(CoreIndex(1), ParaId::from(1)),
(CoreIndex(2), ParaId::from(1)),
(CoreIndex(3), ParaId::from(2)),
(CoreIndex(4), ParaId::from(2)),
(CoreIndex(5), ParaId::from(2)),
]
);
let mut scheduled: BTreeMap<ParaId, BTreeSet<CoreIndex>> = BTreeMap::new();
for (core_idx, para_id) in Pezpallet::<Test>::eligible_paras(&Default::default()) {
scheduled.entry(para_id).or_default().insert(core_idx);
}
assert_eq!(
shared::ActiveValidatorIndices::<Test>::get(),
vec![
ValidatorIndex(0),
ValidatorIndex(1),
ValidatorIndex(2),
ValidatorIndex(3),
ValidatorIndex(4),
ValidatorIndex(5)
]
);
TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
}
}
#[test]
fn happy_path_one_core_per_para() {
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
expected_backed_candidates_with_core,
scheduled_paras: scheduled,
} = get_test_data_one_core_per_para(BackingKind::Threshold);
assert_eq!(
sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
false,
),
expected_backed_candidates_with_core,
);
});
}
#[rstest]
#[case(false)]
#[case(true)]
fn test_with_multiple_cores_per_para(#[case] v2_descriptor: bool) {
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
expected_backed_candidates_with_core,
scheduled_paras: scheduled,
} = get_test_data_multiple_cores_per_para(v2_descriptor);
assert_eq!(
sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
v2_descriptor,
),
expected_backed_candidates_with_core,
);
});
}
#[test]
fn test_candidate_ordering() {
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
} = get_test_data_for_order_checks();
assert_eq!(
sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
false,
),
expected_backed_candidates_with_core
);
});
}
#[test]
fn test_candidate_relay_parent_ordering() {
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
} = get_test_data_for_relay_parent_ordering();
assert_eq!(
sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
false,
),
expected_backed_candidates_with_core
);
});
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
} = get_test_data_for_relay_parent_ordering();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(1),
BlockNumberFor::<Test>::from(4u32),
)
.unwrap();
paras::Pezpallet::<Test>::force_set_most_recent_context(
RuntimeOrigin::root(),
ParaId::from(2),
BlockNumberFor::<Test>::from(2u32),
)
.unwrap();
let res = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
false,
);
assert_eq!(res.len(), 1);
assert_eq!(
expected_backed_candidates_with_core.get(&ParaId::from(2)),
res.get(&ParaId::from(2)),
);
});
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
expected_backed_candidates_with_core,
} = get_test_data_for_relay_parent_ordering();
let mut candidates = VecDeque::new();
let mut commitments = backed_candidates[0].candidate().commitments.clone();
commitments.head_data = paras::Heads::<Test>::get(&ParaId::from(1)).unwrap();
candidates.push_back(inclusion::CandidatePendingAvailability::new(
CoreIndex(0),
CandidateHash(Hash::repeat_byte(1)),
backed_candidates[0].descriptor().clone(),
commitments,
Default::default(),
Default::default(),
4,
4,
GroupIndex(0),
));
inclusion::PendingAvailability::<Test>::insert(ParaId::from(1), candidates);
let mut candidates = VecDeque::new();
let mut commitments = backed_candidates[3].candidate().commitments.clone();
commitments.head_data = paras::Heads::<Test>::get(&ParaId::from(2)).unwrap();
candidates.push_back(inclusion::CandidatePendingAvailability::new(
CoreIndex(0),
CandidateHash(Hash::repeat_byte(2)),
backed_candidates[3].descriptor().clone(),
commitments,
Default::default(),
Default::default(),
2,
2,
GroupIndex(3),
));
inclusion::PendingAvailability::<Test>::insert(ParaId::from(2), candidates);
let res = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
false,
);
assert_eq!(res.len(), 1);
assert_eq!(
expected_backed_candidates_with_core.get(&ParaId::from(2)),
res.get(&ParaId::from(2)),
);
});
}
#[rstest]
#[case(false, true)]
#[case(true, true)]
#[case(false, false)]
#[case(true, false)]
fn nothing_scheduled(#[case] multiple_cores_per_para: bool, #[case] v2_descriptor: bool) {
new_test_ext(default_config()).execute_with(|| {
let TestData { backed_candidates, .. } = if multiple_cores_per_para {
get_test_data_multiple_cores_per_para(v2_descriptor)
} else {
get_test_data_one_core_per_para(BackingKind::Threshold)
};
let scheduled = BTreeMap::new();
let sanitized_backed_candidates = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
BTreeSet::new(),
scheduled,
v2_descriptor,
);
assert!(sanitized_backed_candidates.is_empty());
});
}
#[test]
fn concluded_invalid_are_filtered_out_single_core_per_para() {
new_test_ext(default_config()).execute_with(|| {
let TestData { backed_candidates, scheduled_paras: scheduled, .. } =
get_test_data_one_core_per_para(BackingKind::Threshold);
let set = {
let mut set = std::collections::BTreeSet::new();
for (idx, backed_candidate) in backed_candidates.iter().enumerate() {
if idx & 0x01 == 0 {
set.insert(backed_candidate.hash());
}
}
set
};
let sanitized_backed_candidates: BTreeMap<
ParaId,
Vec<(BackedCandidate<_>, CoreIndex)>,
> = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
set,
scheduled,
false,
);
assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2);
});
}
#[rstest]
#[case(true)]
#[case(false)]
fn concluded_invalid_are_filtered_out_multiple_cores_per_para(#[case] v2_descriptor: bool) {
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
mut expected_backed_candidates_with_core,
..
} = get_test_data_multiple_cores_per_para(v2_descriptor);
let mut invalid_set = std::collections::BTreeSet::new();
for (idx, backed_candidate) in backed_candidates.iter().enumerate() {
if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 0 {
invalid_set.insert(backed_candidate.hash());
} else if backed_candidate.descriptor().para_id() == ParaId::from(3) {
invalid_set.insert(backed_candidate.hash());
}
}
let sanitized_backed_candidates: BTreeMap<
ParaId,
Vec<(BackedCandidate<_>, CoreIndex)>,
> = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
invalid_set,
scheduled,
v2_descriptor,
);
expected_backed_candidates_with_core.remove(&ParaId::from(1)).unwrap();
expected_backed_candidates_with_core.remove(&ParaId::from(3)).unwrap();
assert_eq!(sanitized_backed_candidates, sanitized_backed_candidates);
});
new_test_ext(default_config()).execute_with(|| {
let TestData {
backed_candidates,
scheduled_paras: scheduled,
mut expected_backed_candidates_with_core,
..
} = get_test_data_multiple_cores_per_para(v2_descriptor);
let mut invalid_set = std::collections::BTreeSet::new();
for (idx, backed_candidate) in backed_candidates.iter().enumerate() {
if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 1 {
invalid_set.insert(backed_candidate.hash());
}
}
let sanitized_backed_candidates: BTreeMap<
ParaId,
Vec<(BackedCandidate<_>, CoreIndex)>,
> = sanitize_backed_candidates::<Test>(
backed_candidates.clone(),
&shared::AllowedRelayParents::<Test>::get(),
invalid_set,
scheduled,
v2_descriptor,
);
expected_backed_candidates_with_core
.get_mut(&ParaId::from(1))
.unwrap()
.remove(1);
assert_eq!(sanitized_backed_candidates, expected_backed_candidates_with_core);
});
}
#[test]
fn disabled_non_signing_validator_doesnt_get_filtered() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_one_core_per_para(BackingKind::Threshold);
set_disabled_validators(vec![7]);
let before = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
assert_eq!(expected_backed_candidates_with_core, before);
});
}
#[test]
fn drop_statements_from_disabled_without_dropping_candidate() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_one_core_per_para(BackingKind::Threshold);
set_disabled_validators(vec![0]);
let mut hc = configuration::ActiveConfig::<Test>::get();
hc.minimum_backing_votes = 1;
configuration::Pezpallet::<Test>::force_set_active_config(hc);
assert_eq!(
expected_backed_candidates_with_core
.get(&ParaId::from(1))
.unwrap()
.iter()
.next()
.unwrap()
.0
.validity_votes()
.len(),
2
);
let (validator_indices, maybe_core_index) = expected_backed_candidates_with_core
.get(&ParaId::from(1))
.unwrap()
.iter()
.next()
.unwrap()
.0
.validator_indices_and_core_index();
assert!(maybe_core_index.is_some());
assert_eq!(validator_indices.get(0).unwrap(), true);
assert_eq!(validator_indices.get(1).unwrap(), true);
let untouched = expected_backed_candidates_with_core
.get(&ParaId::from(2))
.unwrap()
.iter()
.next()
.unwrap()
.0
.clone();
let before = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
assert_eq!(before.len(), expected_backed_candidates_with_core.len());
let (validator_indices, maybe_core_index) = expected_backed_candidates_with_core
.get(&ParaId::from(1))
.unwrap()
.iter()
.next()
.unwrap()
.0
.validator_indices_and_core_index();
assert!(maybe_core_index.is_some());
assert_eq!(expected_backed_candidates_with_core.len(), 2);
assert_eq!(
expected_backed_candidates_with_core
.get(&ParaId::from(1))
.unwrap()
.iter()
.next()
.unwrap()
.0
.validity_votes()
.len(),
1
);
assert_eq!(validator_indices.get(0).unwrap(), false);
assert_eq!(validator_indices.get(1).unwrap(), true);
assert_eq!(
expected_backed_candidates_with_core
.get(&ParaId::from(2))
.unwrap()
.iter()
.next()
.unwrap()
.0,
untouched
);
});
}
#[test]
fn drop_candidate_if_all_statements_are_from_disabled_single_core_per_para() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_one_core_per_para(BackingKind::Threshold);
set_disabled_validators(vec![0, 1]);
assert_eq!(
expected_backed_candidates_with_core
.get(&ParaId::from(1))
.unwrap()
.iter()
.next()
.unwrap()
.0
.validity_votes()
.len(),
2
);
let untouched = expected_backed_candidates_with_core
.get(&ParaId::from(2))
.unwrap()
.iter()
.next()
.unwrap()
.0
.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
assert_eq!(expected_backed_candidates_with_core.len(), 1);
assert_eq!(
expected_backed_candidates_with_core
.get(&ParaId::from(2))
.unwrap()
.iter()
.next()
.unwrap()
.0,
untouched
);
assert_eq!(expected_backed_candidates_with_core.get(&ParaId::from(1)), None);
});
}
#[test]
fn drop_candidate_if_all_statements_are_from_disabled_multiple_cores_per_para() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_multiple_cores_per_para(false);
set_disabled_validators(vec![1]);
let mut untouched = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
untouched.get_mut(&ParaId::from(1)).unwrap().remove(1);
assert_eq!(expected_backed_candidates_with_core, untouched);
});
for disabled in [vec![0], vec![0, 1]] {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_multiple_cores_per_para(false);
set_disabled_validators(disabled);
let mut untouched = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
untouched.remove(&ParaId::from(1)).unwrap();
assert_eq!(expected_backed_candidates_with_core, untouched);
});
}
}
#[test]
fn drop_right_vote_basic() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_one_core_per_para(BackingKind::Unanimous);
set_disabled_validators(vec![3]);
let (backed, _) = expected_backed_candidates_with_core
.get_mut(&ParaId::from(1))
.unwrap()
.first_mut()
.unwrap();
let (indices, core) = backed.validator_indices_and_core_index();
let mut indices = BitVec::<_>::from(indices);
indices.set(0, false);
indices.set(1, false);
backed.validity_votes_mut().remove(0);
backed.validity_votes_mut().remove(1);
backed.set_validator_indices_and_core_index(indices, core);
let mut untouched = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
untouched.remove(&ParaId::from(1)).unwrap();
assert_eq!(expected_backed_candidates_with_core, untouched);
});
}
#[test]
fn drop_right_vote_and_process_candidates() {
new_test_ext(default_config()).execute_with(|| {
let TestData { mut expected_backed_candidates_with_core, .. } =
get_test_data_one_core_per_para(BackingKind::Unanimous);
set_disabled_validators(vec![1]);
let (backed, _) = expected_backed_candidates_with_core
.get_mut(&ParaId::from(1))
.unwrap()
.first_mut()
.unwrap();
let (indices, core) = backed.validator_indices_and_core_index();
let mut indices = BitVec::<_>::from(indices);
indices.set(0, false);
backed.validity_votes_mut().remove(0);
backed.set_validator_indices_and_core_index(indices, core);
let untouched = expected_backed_candidates_with_core.clone();
filter_backed_statements_from_disabled_validators::<Test>(
&mut expected_backed_candidates_with_core,
&shared::AllowedRelayParents::<Test>::get(),
);
let candidate_receipt_with_backing_validator_indices =
inclusion::Pezpallet::<Test>::process_candidates(
&shared::AllowedRelayParents::<Test>::get(),
&expected_backed_candidates_with_core,
scheduler::Pezpallet::<Test>::group_validators,
)
.unwrap();
assert_eq!(candidate_receipt_with_backing_validator_indices.len(), untouched.len());
});
}
}
}