mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +00:00
Elastic scaling: use an assumed CoreIndex in candidate-backing (#3229)
First step in implementing https://github.com/paritytech/polkadot-sdk/issues/3144 ### Summary of changes - switch statement `Table` candidate mapping from `ParaId` to `CoreIndex` - introduce experimental `InjectCoreIndex` node feature. - determine and assume a `CoreIndex` for a candidate based on statement validator index. If the signature is valid it means validator controls the validator that index and we can easily map it to a validator group/core. - introduce a temporary provisioner fix until we fully enable elastic scaling in the subystem. The fix ensures we don't fetch the same backable candidate when calling `get_backable_candidate` for each core. TODO: - [x] fix backing tests - [x] fix statement table tests - [x] add new test --------- Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Signed-off-by: alindima <alin@parity.io> Co-authored-by: alindima <alin@parity.io>
This commit is contained in:
@@ -65,13 +65,14 @@ fn dummy_pvd() -> PersistedValidationData {
|
||||
}
|
||||
}
|
||||
|
||||
struct TestState {
|
||||
pub(crate) struct TestState {
|
||||
chain_ids: Vec<ParaId>,
|
||||
keystore: KeystorePtr,
|
||||
validators: Vec<Sr25519Keyring>,
|
||||
validator_public: Vec<ValidatorId>,
|
||||
validation_data: PersistedValidationData,
|
||||
validator_groups: (Vec<Vec<ValidatorIndex>>, GroupRotationInfo),
|
||||
validator_to_group: IndexedVec<ValidatorIndex, Option<GroupIndex>>,
|
||||
availability_cores: Vec<CoreState>,
|
||||
head_data: HashMap<ParaId, HeadData>,
|
||||
signing_context: SigningContext,
|
||||
@@ -114,6 +115,11 @@ impl Default for TestState {
|
||||
.into_iter()
|
||||
.map(|g| g.into_iter().map(ValidatorIndex).collect())
|
||||
.collect();
|
||||
let validator_to_group: IndexedVec<_, _> =
|
||||
vec![Some(0), Some(1), Some(0), Some(0), None, Some(0)]
|
||||
.into_iter()
|
||||
.map(|x| x.map(|x| GroupIndex(x)))
|
||||
.collect();
|
||||
let group_rotation_info =
|
||||
GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 100, now: 1 };
|
||||
|
||||
@@ -143,6 +149,7 @@ impl Default for TestState {
|
||||
validators,
|
||||
validator_public,
|
||||
validator_groups: (validator_groups, group_rotation_info),
|
||||
validator_to_group,
|
||||
availability_cores,
|
||||
head_data,
|
||||
validation_data,
|
||||
@@ -285,6 +292,16 @@ async fn test_startup(virtual_overseer: &mut VirtualOverseer, test_state: &TestS
|
||||
}
|
||||
);
|
||||
|
||||
// Node features request from runtime: all features are disabled.
|
||||
assert_matches!(
|
||||
virtual_overseer.recv().await,
|
||||
AllMessages::RuntimeApi(
|
||||
RuntimeApiMessage::Request(_parent, RuntimeApiRequest::NodeFeatures(_session_index, tx))
|
||||
) => {
|
||||
tx.send(Ok(Default::default())).unwrap();
|
||||
}
|
||||
);
|
||||
|
||||
// Check if subsystem job issues a request for the minimum backing votes.
|
||||
assert_matches!(
|
||||
virtual_overseer.recv().await,
|
||||
@@ -639,6 +656,107 @@ fn backing_works() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_core_index_from_statement_works() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
let pov_a = PoV { block_data: BlockData(vec![42, 43, 44]) };
|
||||
let pvd_a = dummy_pvd();
|
||||
let validation_code_a = ValidationCode(vec![1, 2, 3]);
|
||||
|
||||
let pov_hash = pov_a.hash();
|
||||
|
||||
let mut candidate = TestCandidateBuilder {
|
||||
para_id: test_state.chain_ids[0],
|
||||
relay_parent: test_state.relay_parent,
|
||||
pov_hash,
|
||||
erasure_root: make_erasure_root(&test_state, pov_a.clone(), pvd_a.clone()),
|
||||
persisted_validation_data_hash: pvd_a.hash(),
|
||||
validation_code: validation_code_a.0.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
.build();
|
||||
|
||||
let public2 = Keystore::sr25519_generate_new(
|
||||
&*test_state.keystore,
|
||||
ValidatorId::ID,
|
||||
Some(&test_state.validators[2].to_seed()),
|
||||
)
|
||||
.expect("Insert key into keystore");
|
||||
|
||||
let signed_statement_1 = SignedFullStatementWithPVD::sign(
|
||||
&test_state.keystore,
|
||||
StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()),
|
||||
&test_state.signing_context,
|
||||
ValidatorIndex(2),
|
||||
&public2.into(),
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let public1 = Keystore::sr25519_generate_new(
|
||||
&*test_state.keystore,
|
||||
ValidatorId::ID,
|
||||
Some(&test_state.validators[1].to_seed()),
|
||||
)
|
||||
.expect("Insert key into keystore");
|
||||
|
||||
let signed_statement_2 = SignedFullStatementWithPVD::sign(
|
||||
&test_state.keystore,
|
||||
StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()),
|
||||
&test_state.signing_context,
|
||||
ValidatorIndex(1),
|
||||
&public1.into(),
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
candidate.descriptor.para_id = test_state.chain_ids[1];
|
||||
|
||||
let signed_statement_3 = SignedFullStatementWithPVD::sign(
|
||||
&test_state.keystore,
|
||||
StatementWithPVD::Seconded(candidate, pvd_a.clone()),
|
||||
&test_state.signing_context,
|
||||
ValidatorIndex(1),
|
||||
&public1.into(),
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let core_index_1 = core_index_from_statement(
|
||||
&test_state.validator_to_group,
|
||||
&test_state.validator_groups.1,
|
||||
&test_state.availability_cores,
|
||||
&signed_statement_1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(core_index_1, CoreIndex(0));
|
||||
|
||||
let core_index_2 = core_index_from_statement(
|
||||
&test_state.validator_to_group,
|
||||
&test_state.validator_groups.1,
|
||||
&test_state.availability_cores,
|
||||
&signed_statement_2,
|
||||
);
|
||||
|
||||
// Must be none, para_id in descriptor is different than para assigned to core
|
||||
assert_eq!(core_index_2, None);
|
||||
|
||||
let core_index_3 = core_index_from_statement(
|
||||
&test_state.validator_to_group,
|
||||
&test_state.validator_groups.1,
|
||||
&test_state.availability_cores,
|
||||
&signed_statement_3,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(core_index_3, CoreIndex(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backing_works_while_validation_ongoing() {
|
||||
let test_state = TestState::default();
|
||||
@@ -1422,7 +1540,7 @@ fn backing_works_after_failed_validation() {
|
||||
fn candidate_backing_reorders_votes() {
|
||||
use sp_core::Encode;
|
||||
|
||||
let para_id = ParaId::from(10);
|
||||
let core_idx = CoreIndex(10);
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
@@ -1436,7 +1554,7 @@ fn candidate_backing_reorders_votes() {
|
||||
let validator_groups = {
|
||||
let mut validator_groups = HashMap::new();
|
||||
validator_groups
|
||||
.insert(para_id, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect());
|
||||
.insert(core_idx, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect());
|
||||
validator_groups
|
||||
};
|
||||
|
||||
@@ -1466,10 +1584,10 @@ fn candidate_backing_reorders_votes() {
|
||||
(ValidatorIndex(3), fake_attestation(3)),
|
||||
(ValidatorIndex(1), fake_attestation(1)),
|
||||
],
|
||||
group_id: para_id,
|
||||
group_id: core_idx,
|
||||
};
|
||||
|
||||
let backed = table_attested_to_backed(attested, &table_context).unwrap();
|
||||
let backed = table_attested_to_backed(attested, &table_context, false).unwrap();
|
||||
|
||||
let expected_bitvec = {
|
||||
let mut validator_indices = BitVec::<u8, bitvec::order::Lsb0>::with_capacity(6);
|
||||
|
||||
@@ -185,6 +185,16 @@ async fn activate_leaf(
|
||||
}
|
||||
);
|
||||
|
||||
// Node features request from runtime: all features are disabled.
|
||||
assert_matches!(
|
||||
virtual_overseer.recv().await,
|
||||
AllMessages::RuntimeApi(
|
||||
RuntimeApiMessage::Request(parent, RuntimeApiRequest::NodeFeatures(_session_index, tx))
|
||||
) if parent == hash => {
|
||||
tx.send(Ok(Default::default())).unwrap();
|
||||
}
|
||||
);
|
||||
|
||||
// Check if subsystem job issues a request for the minimum backing votes.
|
||||
assert_matches!(
|
||||
virtual_overseer.recv().await,
|
||||
@@ -305,10 +315,11 @@ async fn assert_hypothetical_frontier_requests(
|
||||
) => {
|
||||
let idx = match expected_requests.iter().position(|r| r.0 == request) {
|
||||
Some(idx) => idx,
|
||||
None => panic!(
|
||||
None =>
|
||||
panic!(
|
||||
"unexpected hypothetical frontier request, no match found for {:?}",
|
||||
request
|
||||
),
|
||||
),
|
||||
};
|
||||
let resp = std::mem::take(&mut expected_requests[idx].1);
|
||||
tx.send(resp).unwrap();
|
||||
@@ -1268,6 +1279,7 @@ fn concurrent_dependent_candidates() {
|
||||
let statement_b = CandidateBackingMessage::Statement(leaf_parent, signed_b.clone());
|
||||
|
||||
virtual_overseer.send(FromOrchestra::Communication { msg: statement_a }).await;
|
||||
|
||||
// At this point the subsystem waits for response, the previous message is received,
|
||||
// send a second one without blocking.
|
||||
let _ = virtual_overseer
|
||||
@@ -1388,7 +1400,19 @@ fn concurrent_dependent_candidates() {
|
||||
assert_eq!(sess_idx, 1);
|
||||
tx.send(Ok(Some(ExecutorParams::default()))).unwrap();
|
||||
},
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
_parent,
|
||||
RuntimeApiRequest::ValidatorGroups(tx),
|
||||
)) => {
|
||||
tx.send(Ok(test_state.validator_groups.clone())).unwrap();
|
||||
},
|
||||
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
_parent,
|
||||
RuntimeApiRequest::AvailabilityCores(tx),
|
||||
)) => {
|
||||
tx.send(Ok(test_state.availability_cores.clone())).unwrap();
|
||||
},
|
||||
_ => panic!("unexpected message received from overseer: {:?}", msg),
|
||||
}
|
||||
}
|
||||
@@ -1419,7 +1443,6 @@ fn seconding_sanity_check_occupy_same_depth() {
|
||||
let leaf_parent = get_parent_hash(leaf_hash);
|
||||
|
||||
let activated = new_leaf(leaf_hash, LEAF_BLOCK_NUMBER);
|
||||
|
||||
let min_block_number = LEAF_BLOCK_NUMBER - LEAF_ANCESTRY_LEN;
|
||||
let min_relay_parents = vec![(para_id_a, min_block_number), (para_id_b, min_block_number)];
|
||||
let test_leaf_a = TestLeaf { activated, min_relay_parents };
|
||||
@@ -1555,13 +1578,14 @@ fn occupied_core_assignment() {
|
||||
const LEAF_A_BLOCK_NUMBER: BlockNumber = 100;
|
||||
const LEAF_A_ANCESTRY_LEN: BlockNumber = 3;
|
||||
let para_id = test_state.chain_ids[0];
|
||||
let previous_para_id = test_state.chain_ids[1];
|
||||
|
||||
// Set the core state to occupied.
|
||||
let mut candidate_descriptor = ::test_helpers::dummy_candidate_descriptor(Hash::zero());
|
||||
candidate_descriptor.para_id = para_id;
|
||||
candidate_descriptor.para_id = previous_para_id;
|
||||
test_state.availability_cores[0] = CoreState::Occupied(OccupiedCore {
|
||||
group_responsible: Default::default(),
|
||||
next_up_on_available: None,
|
||||
next_up_on_available: Some(ScheduledCore { para_id, collator: None }),
|
||||
occupied_since: 100_u32,
|
||||
time_out_at: 200_u32,
|
||||
next_up_on_time_out: None,
|
||||
|
||||
Reference in New Issue
Block a user