backing: move the min votes threshold to the runtime (#1200)

* move min backing votes const to runtime

also cache it per-session in the backing subsystem

Signed-off-by: alindima <alin@parity.io>

* add runtime migration

* introduce api versioning for min_backing votes

also enable it for rococo/versi for testing

* also add min_backing_votes runtime calls to statement-distribution

this dependency has been recently introduced by async backing

* remove explicit version runtime API call

this is not needed, as the RuntimeAPISubsystem already takes care
of versioning and will return NotSupported if the version is not
right.

* address review comments

- parametrise backing votes runtime API with session index
- remove RuntimeInfo usage in backing subsystem, as runtime API
caches the min backing votes by session index anyway.
- move the logic for adjusting the configured needed backing votes with the size of the backing group
to a primitives helper.
- move the legacy min backing votes value to a primitives helper.
- mark JoinMultiple error as fatal, since the Canceled (non-multiple) counterpart is also fatal.
- make backing subsystem handle fatal errors for new leaves update.
- add HostConfiguration consistency check for zeroed backing votes threshold
- add cumulus accompanying change

* fix cumulus test compilation

* fix tests

* more small fixes

* fix merge

* bump runtime api version for westend and rollback version for rococo

---------

Signed-off-by: alindima <alin@parity.io>
Co-authored-by: Javier Viola <javier@parity.io>
This commit is contained in:
Alin Dima
2023-08-31 14:01:36 +03:00
committed by GitHub
parent f1845f725d
commit d6af073aa5
37 changed files with 920 additions and 255 deletions
@@ -948,8 +948,11 @@ fn default_header() -> primitives::Header {
mod sanitizers {
use super::*;
use crate::inclusion::tests::{
back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder,
use crate::{
inclusion::tests::{
back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder,
},
mock::{new_test_ext, MockGenesisConfig},
};
use bitvec::order::Lsb0;
use primitives::{
@@ -1207,131 +1210,133 @@ mod sanitizers {
#[test]
fn candidates() {
const RELAY_PARENT_NUM: u32 = 3;
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
const RELAY_PARENT_NUM: u32 = 3;
let header = default_header();
let relay_parent = header.hash();
let session_index = SessionIndex::from(0_u32);
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 keystore = LocalKeystore::in_memory();
let keystore = Arc::new(keystore) as KeystorePtr;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
keyring::Sr25519Keyring::Alice,
keyring::Sr25519Keyring::Bob,
keyring::Sr25519Keyring::Charlie,
keyring::Sr25519Keyring::Dave,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
PARACHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
let has_concluded_invalid =
|_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false };
let entry_ttl = 10_000;
let scheduled = (0_usize..2)
.into_iter()
.map(|idx| {
let core_idx = CoreIndex::from(idx as u32);
let ca = CoreAssignment {
paras_entry: ParasEntry::new(
Assignment::new(ParaId::from(1_u32 + idx as u32)),
entry_ttl,
),
core: core_idx,
};
ca
})
.collect::<Vec<_>>();
let group_validators = |group_index: GroupIndex| {
match group_index {
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]),
group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]),
_ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"),
let validators = vec![
keyring::Sr25519Keyring::Alice,
keyring::Sr25519Keyring::Bob,
keyring::Sr25519Keyring::Charlie,
keyring::Sr25519Keyring::Dave,
];
for validator in validators.iter() {
Keystore::sr25519_generate_new(
&*keystore,
PARACHAIN_KEY_TYPE_ID,
Some(&validator.to_seed()),
)
.unwrap();
}
.map(|m| m.into_iter().map(ValidatorIndex).collect::<Vec<_>>())
};
let backed_candidates = (0_usize..2)
.into_iter()
.map(|idx0| {
let idx1 = idx0 + 1;
let mut candidate = TestCandidateBuilder {
para_id: ParaId::from(idx1),
relay_parent,
pov_hash: Hash::repeat_byte(idx1 as u8),
persisted_validation_data_hash: [42u8; 32].into(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}
.build();
collator_sign_candidate(Sr25519Keyring::One, &mut candidate);
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(idx0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
);
backed
})
.collect::<Vec<_>>();
// happy path
assert_eq!(
sanitize_backed_candidates::<Test, _>(
backed_candidates.clone(),
has_concluded_invalid,
&scheduled
),
backed_candidates
);
// nothing is scheduled, so no paraids match, thus all backed candidates are skipped
{
let scheduled = &Vec::new();
assert!(sanitize_backed_candidates::<Test, _>(
backed_candidates.clone(),
has_concluded_invalid,
&scheduled
)
.is_empty());
}
// candidates that have concluded as invalid are filtered out
{
// mark every second one as concluded invalid
let set = {
let mut set = std::collections::HashSet::new();
for (idx, backed_candidate) in backed_candidates.iter().enumerate() {
if idx & 0x01 == 0 {
set.insert(backed_candidate.hash());
}
}
set
};
let has_concluded_invalid =
|_idx: usize, candidate: &BackedCandidate| set.contains(&candidate.hash());
|_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false };
let entry_ttl = 10_000;
let scheduled = (0_usize..2)
.into_iter()
.map(|idx| {
let core_idx = CoreIndex::from(idx as u32);
let ca = CoreAssignment {
paras_entry: ParasEntry::new(
Assignment::new(ParaId::from(1_u32 + idx as u32)),
entry_ttl,
),
core: core_idx,
};
ca
})
.collect::<Vec<_>>();
let group_validators = |group_index: GroupIndex| {
match group_index {
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]),
group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]),
_ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"),
}
.map(|m| m.into_iter().map(ValidatorIndex).collect::<Vec<_>>())
};
let backed_candidates = (0_usize..2)
.into_iter()
.map(|idx0| {
let idx1 = idx0 + 1;
let mut candidate = TestCandidateBuilder {
para_id: ParaId::from(idx1),
relay_parent,
pov_hash: Hash::repeat_byte(idx1 as u8),
persisted_validation_data_hash: [42u8; 32].into(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}
.build();
collator_sign_candidate(Sr25519Keyring::One, &mut candidate);
let backed = back_candidate(
candidate,
&validators,
group_validators(GroupIndex::from(idx0 as u32)).unwrap().as_ref(),
&keystore,
&signing_context,
BackingKind::Threshold,
);
backed
})
.collect::<Vec<_>>();
// happy path
assert_eq!(
sanitize_backed_candidates::<Test, _>(
backed_candidates.clone(),
has_concluded_invalid,
&scheduled
)
.len(),
backed_candidates.len() / 2
),
backed_candidates
);
}
// nothing is scheduled, so no paraids match, thus all backed candidates are skipped
{
let scheduled = &Vec::new();
assert!(sanitize_backed_candidates::<Test, _>(
backed_candidates.clone(),
has_concluded_invalid,
&scheduled
)
.is_empty());
}
// candidates that have concluded as invalid are filtered out
{
// mark every second one as concluded invalid
let set = {
let mut set = std::collections::HashSet::new();
for (idx, backed_candidate) in backed_candidates.iter().enumerate() {
if idx & 0x01 == 0 {
set.insert(backed_candidate.hash());
}
}
set
};
let has_concluded_invalid =
|_idx: usize, candidate: &BackedCandidate| set.contains(&candidate.hash());
assert_eq!(
sanitize_backed_candidates::<Test, _>(
backed_candidates.clone(),
has_concluded_invalid,
&scheduled
)
.len(),
backed_candidates.len() / 2
);
}
});
}
}