Use a more typesafe approach for managing indexed data (#6150)

* Fix for issue #2403

* Nightly fmt

* Quick documentation fixes

* Default Implementation

* iter() function integrated

* Implemented iter functionalities

* Fmt

* small change

* updates node-network

* updates in dispute-coordinator

* Updates

* benchmarking fix

* minor fix

* test fixes in runtime api

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Removal of [index], shorting of FromIterator, Renaming of GroupValidators to ValidatorGroups

* Removal of ops import

* documentation fixes for spell check

* implementation of generic type

* Refactoring

* Test and documentation fixes

* minor test fix

* minor test fix

* minor test fix

* Update node/network/statement-distribution/src/lib.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* removed IterMut

* Update node/core/dispute-coordinator/src/import.rs

Co-authored-by: Andronik <write@reusable.software>

* Update node/core/dispute-coordinator/src/initialized.rs

Co-authored-by: Andronik <write@reusable.software>

* Update primitives/src/v2/mod.rs

Co-authored-by: Andronik <write@reusable.software>

* fmt

* IterMut

* documentation update

Co-authored-by: Andronik <write@reusable.software>

* minor adjustments and new TypeIndex trait

* spelling fix

* TypeIndex fix

Co-authored-by: Andronik <write@reusable.software>
This commit is contained in:
Boluwatife Bakre
2022-10-22 09:39:11 +01:00
committed by GitHub
parent f8cc39a761
commit 8eb1f4617f
28 changed files with 261 additions and 138 deletions
@@ -21,7 +21,8 @@ use polkadot_node_primitives::approval::{
self as approval_types, AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory,
};
use polkadot_primitives::v2::{
AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, SessionInfo, ValidatorIndex,
AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo,
ValidatorIndex,
};
use sc_keystore::LocalKeystore;
use sp_application_crypto::ByteArray;
@@ -138,7 +139,7 @@ pub(crate) struct Config {
/// The assignment public keys for validators.
assignment_keys: Vec<AssignmentId>,
/// The groups of validators assigned to each core.
validator_groups: Vec<Vec<ValidatorIndex>>,
validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
/// The number of availability cores used by the protocol during this session.
n_cores: u32,
/// The zeroth delay tranche width.
@@ -541,11 +542,11 @@ pub(crate) fn check_assignment_cert(
}
fn is_in_backing_group(
validator_groups: &[Vec<ValidatorIndex>],
validator_groups: &IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
validator: ValidatorIndex,
group: GroupIndex,
) -> bool {
validator_groups.get(group.0 as usize).map_or(false, |g| g.contains(&validator))
validator_groups.get(group).map_or(false, |g| g.contains(&validator))
}
#[cfg(test)]
@@ -590,7 +591,10 @@ mod tests {
.collect()
}
fn basic_groups(n_validators: usize, n_groups: usize) -> Vec<Vec<ValidatorIndex>> {
fn basic_groups(
n_validators: usize,
n_groups: usize,
) -> IndexedVec<GroupIndex, Vec<ValidatorIndex>> {
let size = n_validators / n_groups;
let big_groups = n_validators % n_groups;
let scraps = n_groups * size;
@@ -631,10 +635,10 @@ mod tests {
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
]),
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1), ValidatorIndex(2)],
],
]),
n_cores: 2,
zeroth_delay_tranche_width: 10,
relay_vrf_modulo_samples: 3,
@@ -666,10 +670,10 @@ mod tests {
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
]),
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1), ValidatorIndex(2)],
],
]),
n_cores: 2,
zeroth_delay_tranche_width: 10,
relay_vrf_modulo_samples: 3,
@@ -696,7 +700,7 @@ mod tests {
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
]),
validator_groups: vec![],
validator_groups: Default::default(),
n_cores: 0,
zeroth_delay_tranche_width: 10,
relay_vrf_modulo_samples: 3,
@@ -620,7 +620,9 @@ pub(crate) mod tests {
use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage};
use polkadot_node_subsystem_test_helpers::make_subsystem_context;
use polkadot_node_subsystem_util::database::Database;
use polkadot_primitives::v2::{Id as ParaId, SessionInfo, ValidatorIndex};
use polkadot_primitives::v2::{
Id as ParaId, IndexedVec, SessionInfo, ValidatorId, ValidatorIndex,
};
pub(crate) use sp_consensus_babe::{
digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest},
AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch,
@@ -713,10 +715,10 @@ pub(crate) mod tests {
fn dummy_session_info(index: SessionIndex) -> SessionInfo {
SessionInfo {
validators: Vec::new(),
validators: Default::default(),
discovery_keys: Vec::new(),
assignment_keys: Vec::new(),
validator_groups: Vec::new(),
validator_groups: Default::default(),
n_cores: index as _,
zeroth_delay_tranche_width: index as _,
relay_vrf_modulo_samples: index as _,
@@ -1174,21 +1176,27 @@ pub(crate) mod tests {
let session = 5;
let irrelevant = 666;
let session_info = SessionInfo {
validators: vec![Sr25519Keyring::Alice.public().into(); 6],
discovery_keys: Vec::new(),
assignment_keys: Vec::new(),
validator_groups: vec![vec![ValidatorIndex(0); 5], vec![ValidatorIndex(0); 2]],
n_cores: 6,
needed_approvals: 2,
zeroth_delay_tranche_width: irrelevant,
relay_vrf_modulo_samples: irrelevant,
n_delay_tranches: irrelevant,
no_show_slots: irrelevant,
active_validator_indices: Vec::new(),
dispute_period: 6,
random_seed: [0u8; 32],
};
let session_info =
SessionInfo {
validators: IndexedVec::<ValidatorIndex, ValidatorId>::from(
vec![Sr25519Keyring::Alice.public().into(); 6],
),
discovery_keys: Vec::new(),
assignment_keys: Vec::new(),
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0); 5],
vec![ValidatorIndex(0); 2],
]),
n_cores: 6,
needed_approvals: 2,
zeroth_delay_tranche_width: irrelevant,
relay_vrf_modulo_samples: irrelevant,
n_delay_tranches: irrelevant,
no_show_slots: irrelevant,
active_validator_indices: Vec::new(),
dispute_period: 6,
random_seed: [0u8; 32],
};
let slot = Slot::from(10);
@@ -1803,7 +1803,7 @@ fn check_and_import_approval<T>(
)),
};
let pubkey = match session_info.validators.get(approval.validator.0 as usize) {
let pubkey = match session_info.validators.get(approval.validator) {
Some(k) => k,
None => respond_early!(ApprovalCheckResult::Bad(
ApprovalCheckError::InvalidValidatorIndex(approval.validator),
@@ -2503,7 +2503,7 @@ async fn issue_approval<Context>(
},
};
let validator_pubkey = match session_info.validators.get(validator_index.0 as usize) {
let validator_pubkey = match session_info.validators.get(validator_index) {
Some(p) => p,
None => {
gum::warn!(
+19 -16
View File
@@ -32,7 +32,7 @@ use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_node_subsystem_util::TimeoutExt;
use polkadot_overseer::HeadSupportsParachains;
use polkadot_primitives::v2::{
CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, Id as ParaId,
CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, Id as ParaId, IndexedVec,
ValidationCode, ValidatorSignature,
};
use std::time::Duration;
@@ -739,7 +739,10 @@ fn session_info(keys: &[Sr25519Keyring]) -> SessionInfo {
validators: keys.iter().map(|v| v.public().into()).collect(),
discovery_keys: keys.iter().map(|v| v.public().into()).collect(),
assignment_keys: keys.iter().map(|v| v.public().into()).collect(),
validator_groups: vec![vec![ValidatorIndex(0)], vec![ValidatorIndex(1)]],
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0)],
vec![ValidatorIndex(1)],
]),
n_cores: keys.len() as _,
needed_approvals: 2,
zeroth_delay_tranche_width: 5,
@@ -1552,11 +1555,11 @@ fn subsystem_second_approval_import_only_schedules_wakeups() {
Sr25519Keyring::Eve,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
needed_approvals: 1,
..session_info(&validators)
};
@@ -1889,11 +1892,11 @@ fn import_checked_approval_updates_entries_and_schedules() {
Sr25519Keyring::Eve,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
..session_info(&validators)
};
@@ -2046,11 +2049,11 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() {
Sr25519Keyring::Eve,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
..session_info(&validators)
};
@@ -2336,11 +2339,11 @@ fn subsystem_validate_approvals_cache() {
Sr25519Keyring::Eve,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
..session_info(&validators)
};
@@ -2548,11 +2551,11 @@ where
Sr25519Keyring::Ferdie,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2), ValidatorIndex(3)],
vec![ValidatorIndex(4), ValidatorIndex(5)],
],
]),
relay_vrf_modulo_samples: 2,
no_show_slots,
..session_info(&validators)
@@ -2868,11 +2871,11 @@ fn pre_covers_dont_stall_approval() {
Sr25519Keyring::One,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2), ValidatorIndex(5)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
..session_info(&validators)
};
@@ -3045,11 +3048,11 @@ fn waits_until_approving_assignments_are_old_enough() {
Sr25519Keyring::One,
];
let session_info = SessionInfo {
validator_groups: vec![
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2), ValidatorIndex(5)],
vec![ValidatorIndex(3), ValidatorIndex(4)],
],
]),
..session_info(&validators)
};
@@ -31,8 +31,8 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use polkadot_node_primitives::{CandidateVotes, SignedDisputeStatement};
use polkadot_node_subsystem_util::rolling_session_window::RollingSessionWindow;
use polkadot_primitives::v2::{
CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo, ValidDisputeStatementKind,
ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature,
CandidateReceipt, DisputeStatement, IndexedVec, SessionIndex, SessionInfo,
ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature,
};
use sc_keystore::LocalKeystore;
@@ -63,7 +63,7 @@ impl<'a> CandidateEnvironment<'a> {
}
/// Validators in the candidate's session.
pub fn validators(&self) -> &Vec<ValidatorId> {
pub fn validators(&self) -> &IndexedVec<ValidatorIndex, ValidatorId> {
&self.session.validators
}
@@ -229,7 +229,7 @@ impl CandidateVoteState<CandidateVotes> {
for (statement, val_index) in statements {
if env
.validators()
.get(val_index.0 as usize)
.get(val_index)
.map_or(true, |v| v != statement.validator_public())
{
gum::error!(
@@ -488,7 +488,7 @@ impl ImportResult {
for (index, sig) in approval_votes.into_iter() {
debug_assert!(
{
let pub_key = &env.session_info().validators[index.0 as usize];
let pub_key = &env.session_info().validators.get(index).expect("indices are validated by approval-voting subsystem; qed");
let candidate_hash = votes.candidate_receipt.hash();
let session_index = env.session_index();
DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking)
@@ -538,7 +538,7 @@ impl ImportResult {
/// That is all `ValidatorIndex`es we have private keys for. Usually this will only be one.
fn find_controlled_validator_indices(
keystore: &LocalKeystore,
validators: &[ValidatorId],
validators: &IndexedVec<ValidatorIndex, ValidatorId>,
) -> HashSet<ValidatorIndex> {
let mut controlled = HashSet::new();
for (index, validator) in validators.iter().enumerate() {
@@ -372,7 +372,7 @@ impl Initialized {
.filter_map(|(validator_index, attestation)| {
let validator_public: ValidatorId = session_info
.validators
.get(validator_index.0 as usize)
.get(validator_index)
.or_else(|| {
gum::error!(
target: LOG_TARGET,
@@ -473,7 +473,7 @@ impl Initialized {
let validator_public: ValidatorId = session_info
.validators
.get(validator_index.0 as usize)
.get(validator_index)
.or_else(|| {
gum::error!(
target: LOG_TARGET,
@@ -903,7 +903,7 @@ impl Initialized {
let no_votes = Vec::new();
let our_approval_votes = new_state.own_approval_votes().unwrap_or(&no_votes);
for (validator_index, sig) in our_approval_votes {
let pub_key = match env.validators().get(validator_index.0 as usize) {
let pub_key = match env.validators().get(*validator_index) {
None => {
gum::error!(
target: LOG_TARGET,
@@ -1097,7 +1097,10 @@ impl Initialized {
valid,
candidate_hash,
session,
env.validators()[index.0 as usize].clone(),
env.validators()
.get(*index)
.expect("`controlled_indices` are derived from `validators`; qed")
.clone(),
)
.await;
@@ -1238,7 +1241,7 @@ fn make_dispute_message(
our_vote.candidate_hash().clone(),
our_vote.session_index(),
validators
.get(validator_index.0 as usize)
.get(*validator_index)
.ok_or(DisputeMessageCreationError::InvalidValidatorIndex)?
.clone(),
validator_signature.clone(),
@@ -1253,7 +1256,7 @@ fn make_dispute_message(
our_vote.candidate_hash().clone(),
our_vote.session_index(),
validators
.get(validator_index.0 as usize)
.get(*validator_index)
.ok_or(DisputeMessageCreationError::InvalidValidatorIndex)?
.clone(),
validator_signature.clone(),
@@ -58,9 +58,9 @@ use polkadot_node_subsystem::{
use polkadot_node_subsystem_test_helpers::{make_subsystem_context, TestSubsystemContextHandle};
use polkadot_primitives::v2::{
ApprovalVote, BlockNumber, CandidateCommitments, CandidateHash, CandidateReceipt,
DisputeStatement, Hash, Header, MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex,
SessionInfo, SigningContext, ValidDisputeStatementKind, ValidatorId, ValidatorIndex,
ValidatorSignature,
DisputeStatement, GroupIndex, Hash, Header, IndexedVec, MultiDisputeStatementSet,
ScrapedOnChainVotes, SessionIndex, SessionInfo, SigningContext, ValidDisputeStatementKind,
ValidatorId, ValidatorIndex, ValidatorSignature,
};
use crate::{
@@ -125,8 +125,8 @@ impl MockClock {
struct TestState {
validators: Vec<Pair>,
validator_public: Vec<ValidatorId>,
validator_groups: Vec<Vec<ValidatorIndex>>,
validator_public: IndexedVec<ValidatorIndex, ValidatorId>,
validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
master_keystore: Arc<sc_keystore::LocalKeystore>,
subsystem_keystore: Arc<sc_keystore::LocalKeystore>,
db: Arc<dyn Database>,
@@ -163,11 +163,11 @@ impl Default for TestState {
.map(|k| ValidatorId::from(k.0.public()))
.collect();
let validator_groups = vec![
let validator_groups = IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
vec![ValidatorIndex(0), ValidatorIndex(1)],
vec![ValidatorIndex(2), ValidatorIndex(3)],
vec![ValidatorIndex(4), ValidatorIndex(5), ValidatorIndex(6)],
];
]);
let master_keystore = make_keystore(validators.iter().map(|v| v.1.clone())).into();
let subsystem_keystore =
@@ -431,7 +431,7 @@ impl TestState {
session: SessionIndex,
valid: bool,
) -> SignedDisputeStatement {
let public = self.validator_public[index.0 as usize].clone();
let public = self.validator_public.get(index).unwrap().clone();
let keystore = self.master_keystore.clone() as SyncCryptoStorePtr;
+2 -2
View File
@@ -522,10 +522,10 @@ fn requests_session_index_for_child() {
fn dummy_session_info() -> SessionInfo {
SessionInfo {
validators: vec![],
validators: Default::default(),
discovery_keys: vec![],
assignment_keys: vec![],
validator_groups: vec![],
validator_groups: Default::default(),
n_cores: 4u32,
zeroth_delay_tranche_width: 0u32,
relay_vrf_modulo_samples: 0u32,
@@ -24,8 +24,8 @@ use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks};
use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV, Proof};
use polkadot_primitives::v2::{
CandidateCommitments, CandidateDescriptor, CandidateHash, CommittedCandidateReceipt,
GroupIndex, Hash, HeadData, Id as ParaId, OccupiedCore, PersistedValidationData, SessionInfo,
ValidatorIndex,
GroupIndex, Hash, HeadData, Id as ParaId, IndexedVec, OccupiedCore, PersistedValidationData,
SessionInfo, ValidatorIndex,
};
use polkadot_primitives_test_helpers::{
dummy_collator, dummy_collator_signature, dummy_hash, dummy_validation_code,
@@ -43,10 +43,11 @@ pub fn make_session_info() -> SessionInfo {
Sr25519Keyring::One,
];
let validator_groups: Vec<Vec<ValidatorIndex>> = [vec![5, 0, 3], vec![1, 6, 2, 4]]
.iter()
.map(|g| g.into_iter().map(|v| ValidatorIndex(*v)).collect())
.collect();
let validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>> =
[vec![5, 0, 3], vec![1, 6, 2, 4]]
.iter()
.map(|g| g.into_iter().map(|v| ValidatorIndex(*v)).collect())
.collect();
SessionInfo {
discovery_keys: validators.iter().map(|k| k.public().into()).collect(),
@@ -58,7 +58,7 @@ use polkadot_node_subsystem::{
use polkadot_node_subsystem_util::request_session_info;
use polkadot_primitives::v2::{
AuthorityDiscoveryId, BlakeTwo256, BlockNumber, CandidateHash, CandidateReceipt, GroupIndex,
Hash, HashT, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex,
Hash, HashT, IndexedVec, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex,
};
mod error;
@@ -134,7 +134,7 @@ struct RecoveryParams {
validator_authority_keys: Vec<AuthorityDiscoveryId>,
/// Validators relevant to this `RecoveryTask`.
validators: Vec<ValidatorId>,
validators: IndexedVec<ValidatorIndex, ValidatorId>,
/// The number of pieces needed.
threshold: usize,
@@ -871,7 +871,7 @@ async fn launch_recovery_task<Context>(
};
let phase = backing_group
.and_then(|g| session_info.validator_groups.get(g.0 as usize))
.and_then(|g| session_info.validator_groups.get(g))
.map(|group| Source::RequestFromBackers(RequestFromBackers::new(group.clone())))
.unwrap_or_else(|| {
Source::RequestChunks(RequestChunksFromValidators::new(params.validators.len() as _))
@@ -36,7 +36,9 @@ use polkadot_node_subsystem::{
};
use polkadot_node_subsystem_test_helpers::{make_subsystem_context, TestSubsystemContextHandle};
use polkadot_node_subsystem_util::TimeoutExt;
use polkadot_primitives::v2::{AuthorityDiscoveryId, Hash, HeadData, PersistedValidationData};
use polkadot_primitives::v2::{
AuthorityDiscoveryId, Hash, HeadData, IndexedVec, PersistedValidationData, ValidatorId,
};
use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash};
type VirtualOverseer = TestSubsystemContextHandle<AvailabilityRecoveryMessage>;
@@ -179,7 +181,7 @@ impl Has {
#[derive(Clone)]
struct TestState {
validators: Vec<Sr25519Keyring>,
validator_public: Vec<ValidatorId>,
validator_public: IndexedVec<ValidatorIndex, ValidatorId>,
validator_authority_id: Vec<AuthorityDiscoveryId>,
current: Hash,
candidate: CandidateReceipt,
@@ -218,7 +220,7 @@ impl TestState {
validators: self.validator_public.clone(),
discovery_keys: self.validator_authority_id.clone(),
// all validators in the same group.
validator_groups: vec![(0..self.validators.len()).map(|i| ValidatorIndex(i as _)).collect()],
validator_groups: IndexedVec::<GroupIndex,Vec<ValidatorIndex>>::from(vec![(0..self.validators.len()).map(|i| ValidatorIndex(i as _)).collect()]),
assignment_keys: vec![],
n_cores: 0,
zeroth_delay_tranche_width: 0,
@@ -402,7 +404,7 @@ impl TestState {
}
}
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> IndexedVec<ValidatorIndex, ValidatorId> {
val_ids.iter().map(|v| v.public().into()).collect()
}
@@ -448,10 +448,8 @@ async fn determine_our_validators<Context>(
let rotation_info = get_group_rotation_info(ctx.sender(), relay_parent).await?;
let current_group_index = rotation_info.group_for_core(core_index, cores);
let current_validators = groups
.get(current_group_index.0 as usize)
.map(|v| v.as_slice())
.unwrap_or_default();
let current_validators =
groups.get(current_group_index).map(|v| v.as_slice()).unwrap_or_default();
let validators = &info.discovery_keys;
@@ -44,8 +44,8 @@ use polkadot_node_subsystem::{
use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_node_subsystem_util::TimeoutExt;
use polkadot_primitives::v2::{
AuthorityDiscoveryId, CollatorPair, GroupRotationInfo, ScheduledCore, SessionIndex,
SessionInfo, ValidatorId, ValidatorIndex,
AuthorityDiscoveryId, CollatorPair, GroupIndex, GroupRotationInfo, IndexedVec, ScheduledCore,
SessionIndex, SessionInfo, ValidatorId, ValidatorIndex,
};
use polkadot_primitives_test_helpers::TestCandidateBuilder;
@@ -62,7 +62,7 @@ struct TestState {
session_index: SessionIndex,
}
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> IndexedVec<ValidatorIndex, ValidatorId> {
val_ids.iter().map(|v| v.public().into()).collect()
}
@@ -135,7 +135,7 @@ impl TestState {
fn current_group_validator_indices(&self) -> &[ValidatorIndex] {
let core_num = self.availability_cores.len();
let GroupIndex(group_idx) = self.group_rotation_info.group_for_core(CoreIndex(0), core_num);
&self.session_info.validator_groups[group_idx as usize]
&self.session_info.validator_groups.get(GroupIndex::from(group_idx)).unwrap()
}
fn current_session_index(&self) -> SessionIndex {
@@ -367,7 +367,7 @@ async fn distribute_collation(
)) => {
assert_eq!(relay_parent, test_state.relay_parent);
tx.send(Ok((
test_state.session_info.validator_groups.clone(),
test_state.session_info.validator_groups.to_vec(),
test_state.group_rotation_info.clone(),
)))
.unwrap();
@@ -299,7 +299,7 @@ impl DisputeSender {
let valid_public = info
.session_info
.validators
.get(valid_index.0 as usize)
.get(*valid_index)
.ok_or(JfyiError::InvalidStatementFromCoordinator)?;
let valid_signed = SignedDisputeStatement::new_checked(
DisputeStatement::Valid(kind.clone()),
@@ -314,7 +314,7 @@ impl DisputeSender {
let invalid_public = info
.session_info
.validators
.get(invalid_index.0 as usize)
.get(*invalid_index)
.ok_or(JfyiError::InvalidValidatorIndexFromCoordinator)?;
let invalid_signed = SignedDisputeStatement::new_checked(
DisputeStatement::Invalid(kind.clone()),
@@ -84,7 +84,7 @@ pub static ref MOCK_SESSION_INFO: SessionInfo =
.map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone())
.collect(),
assignment_keys: vec![],
validator_groups: vec![],
validator_groups: Default::default(),
n_cores: 0,
zeroth_delay_tranche_width: 0,
relay_vrf_modulo_samples: 0,
@@ -104,9 +104,9 @@ pub static ref MOCK_NEXT_SESSION_INFO: SessionInfo =
.iter()
.map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone())
.collect(),
validators: vec![],
validators: Default::default(),
assignment_keys: vec![],
validator_groups: vec![],
validator_groups: Default::default(),
n_cores: 0,
zeroth_delay_tranche_width: 0,
relay_vrf_modulo_samples: 0,
@@ -37,6 +37,7 @@ use polkadot_node_subsystem::{
};
use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_node_subsystem_util::TimeoutExt as _;
use polkadot_primitives::v2::{GroupIndex, IndexedVec};
use test_helpers::mock::make_ferdie_keystore;
use super::*;
@@ -219,7 +220,9 @@ fn make_session_info() -> SessionInfo {
validators: AUTHORITY_KEYRINGS.iter().map(|k| k.public().into()).collect(),
discovery_keys: AUTHORITIES.clone(),
assignment_keys: AUTHORITY_KEYRINGS.iter().map(|k| k.public().into()).collect(),
validator_groups: vec![all_validator_indices],
validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(vec![
all_validator_indices,
]),
n_cores: 1,
zeroth_delay_tranche_width: 1,
relay_vrf_modulo_samples: 1,
@@ -47,8 +47,8 @@ use polkadot_node_subsystem::{
};
use polkadot_primitives::v2::{
AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash,
SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex,
ValidatorSignature,
IndexedVec, SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId,
ValidatorIndex, ValidatorSignature,
};
use futures::{
@@ -665,7 +665,7 @@ struct ActiveHeadData {
/// Large statements we are waiting for with associated meta data.
waiting_large_statements: HashMap<CandidateHash, LargeStatementStatus>,
/// The parachain validators at the head's child session index.
validators: Vec<ValidatorId>,
validators: IndexedVec<ValidatorIndex, ValidatorId>,
/// The current session index of this fork.
session_index: sp_staking::SessionIndex,
/// How many `Seconded` statements we've seen per validator.
@@ -676,7 +676,7 @@ struct ActiveHeadData {
impl ActiveHeadData {
fn new(
validators: Vec<ValidatorId>,
validators: IndexedVec<ValidatorIndex, ValidatorId>,
session_index: sp_staking::SessionIndex,
span: PerLeafSpan,
) -> Self {
@@ -878,7 +878,7 @@ fn check_statement_signature(
SigningContext { session_index: head.session_index, parent_hash: relay_parent };
head.validators
.get(statement.unchecked_validator_index().0 as usize)
.get(statement.unchecked_validator_index())
.ok_or_else(|| statement.clone())
.and_then(|v| statement.try_into_checked(&signing_context, v))
}
@@ -2072,7 +2072,10 @@ impl<R: rand::Rng> StatementDistributionSubsystem<R> {
// directly:
let group_peers = {
if let Some(our_group) = validator_info.our_group {
let our_group = &session_info.validator_groups[our_group.0 as usize];
let our_group = &session_info
.validator_groups
.get(our_group)
.expect("`our_group` is derived from `validator_groups`; qed");
our_group
.into_iter()
@@ -35,7 +35,9 @@ use polkadot_node_subsystem::{
ActivatedLeaf, LeafStatus,
};
use polkadot_node_subsystem_test_helpers::mock::make_ferdie_keystore;
use polkadot_primitives::v2::{Hash, Id as ParaId, SessionInfo, ValidationCode};
use polkadot_primitives::v2::{
GroupIndex, Hash, Id as ParaId, IndexedVec, SessionInfo, ValidationCode, ValidatorId,
};
use polkadot_primitives_test_helpers::{
dummy_committed_candidate_receipt, dummy_hash, AlwaysZeroRng,
};
@@ -83,7 +85,7 @@ fn active_head_accepts_only_2_seconded_per_validator() {
};
let mut head_data = ActiveHeadData::new(
validators,
IndexedVec::<ValidatorIndex, ValidatorId>::from(validators),
session_index,
PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"),
);
@@ -429,7 +431,7 @@ fn peer_view_update_sends_messages() {
let new_head_data = {
let mut data = ActiveHeadData::new(
validators,
IndexedVec::<ValidatorIndex, ValidatorId>::from(validators),
session_index,
PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"),
);
@@ -2319,7 +2321,7 @@ fn handle_multiple_seconded_statements() {
}
fn make_session_info(validators: Vec<Pair>, groups: Vec<Vec<u32>>) -> SessionInfo {
let validator_groups: Vec<Vec<ValidatorIndex>> = groups
let validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>> = groups
.iter()
.map(|g| g.into_iter().map(|v| ValidatorIndex(*v)).collect())
.collect();
@@ -135,11 +135,11 @@ impl DisputeMessage {
let valid_id = session_info
.validators
.get(valid_index.0 as usize)
.get(valid_index)
.ok_or(Error::ValidStatementInvalidValidatorIndex)?;
let invalid_id = session_info
.validators
.get(invalid_index.0 as usize)
.get(invalid_index)
.ok_or(Error::InvalidStatementInvalidValidatorIndex)?;
if valid_id != valid_statement.validator_public() {
@@ -223,8 +223,7 @@ impl UncheckedDisputeMessage {
let vote_valid = {
let ValidDisputeVote { validator_index, signature, kind } = valid_vote;
let validator_public =
session_info.validators.get(validator_index.0 as usize).ok_or(())?.clone();
let validator_public = session_info.validators.get(validator_index).ok_or(())?.clone();
(
SignedDisputeStatement::new_checked(
@@ -240,8 +239,7 @@ impl UncheckedDisputeMessage {
let vote_invalid = {
let InvalidDisputeVote { validator_index, signature, kind } = invalid_vote;
let validator_public =
session_info.validators.get(validator_index.0 as usize).ok_or(())?.clone();
let validator_public = session_info.validators.get(validator_index).ok_or(())?.clone();
(
SignedDisputeStatement::new_checked(
@@ -399,10 +399,10 @@ mod tests {
fn dummy_session_info(index: SessionIndex) -> SessionInfo {
SessionInfo {
validators: Vec::new(),
validators: Default::default(),
discovery_keys: Vec::new(),
assignment_keys: Vec::new(),
validator_groups: Vec::new(),
validator_groups: Default::default(),
n_cores: index as _,
zeroth_delay_tranche_width: index as _,
relay_vrf_modulo_samples: index as _,
@@ -27,9 +27,9 @@ use sp_keystore::{CryptoStore, SyncCryptoStorePtr};
use polkadot_node_subsystem::{messages::RuntimeApiMessage, overseer, SubsystemSender};
use polkadot_primitives::v2::{
CandidateEvent, CoreState, EncodeAs, GroupIndex, GroupRotationInfo, Hash, OccupiedCore,
ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, SigningContext, UncheckedSigned,
ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
CandidateEvent, CoreState, EncodeAs, GroupIndex, GroupRotationInfo, Hash, IndexedVec,
OccupiedCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, SigningContext,
UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
};
use crate::{
@@ -228,7 +228,10 @@ impl RuntimeInfo {
/// Get our `ValidatorIndex`.
///
/// Returns: None if we are not a validator.
async fn get_our_index(&self, validators: &[ValidatorId]) -> Option<ValidatorIndex> {
async fn get_our_index(
&self,
validators: &IndexedVec<ValidatorIndex, ValidatorId>,
) -> Option<ValidatorIndex> {
let keystore = self.keystore.as_ref()?;
for (i, v) in validators.iter().enumerate() {
if CryptoStore::has_keys(&**keystore, &[(v.to_raw_vec(), ValidatorId::ID)]).await {
@@ -254,7 +257,7 @@ where
session_info
.validators
.get(signed.unchecked_validator_index().0 as usize)
.get(signed.unchecked_validator_index())
.ok_or_else(|| signed.clone())
.and_then(|v| signed.try_into_checked(&signing_context, v))
}
+100 -5
View File
@@ -19,7 +19,12 @@
use bitvec::vec::BitVec;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_std::prelude::*;
use sp_std::{
marker::PhantomData,
prelude::*,
slice::{Iter, IterMut},
vec::IntoIter,
};
use application_crypto::KeyTypeId;
use inherents::InherentIdentifier;
@@ -123,6 +128,12 @@ impl MallocSizeOf for ValidatorId {
}
}
/// Trait required for type specific indices e.g. `ValidatorIndex` and `GroupIndex`
pub trait TypeIndex {
/// Returns the index associated to this value.
fn type_index(&self) -> usize;
}
/// Index of the validator is used as a lightweight replacement of the `ValidatorId` when appropriate.
#[derive(Eq, Ord, PartialEq, PartialOrd, Copy, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))]
@@ -135,6 +146,12 @@ impl From<u32> for ValidatorIndex {
}
}
impl TypeIndex for ValidatorIndex {
fn type_index(&self) -> usize {
self.0 as usize
}
}
application_crypto::with_pair! {
/// A Parachain validator keypair.
pub type ValidatorPair = validator_app::Pair;
@@ -779,6 +796,12 @@ impl From<u32> for CoreIndex {
}
}
impl TypeIndex for CoreIndex {
fn type_index(&self) -> usize {
self.0 as usize
}
}
/// The unique (during session) index of a validator group.
#[derive(Encode, Decode, Default, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Hash, MallocSizeOf))]
@@ -790,6 +813,12 @@ impl From<u32> for GroupIndex {
}
}
impl TypeIndex for GroupIndex {
fn type_index(&self) -> usize {
self.0 as usize
}
}
/// A claim on authoring the next block for a given parathread.
#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(PartialEq))]
@@ -1569,6 +1598,72 @@ impl CompactStatement {
}
}
/// `IndexedVec` struct indexed by type specific indices.
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(PartialEq, MallocSizeOf))]
pub struct IndexedVec<K, V>(Vec<V>, PhantomData<fn(K) -> K>);
impl<K, V> Default for IndexedVec<K, V> {
fn default() -> Self {
Self(vec![], PhantomData)
}
}
impl<K, V> From<Vec<V>> for IndexedVec<K, V> {
fn from(validators: Vec<V>) -> Self {
Self(validators, PhantomData)
}
}
impl<K, V> FromIterator<V> for IndexedVec<K, V> {
fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
Self(Vec::from_iter(iter), PhantomData)
}
}
impl<K, V> IndexedVec<K, V>
where
V: Clone,
{
/// Returns a reference to an element indexed using `K`.
pub fn get(&self, index: K) -> Option<&V>
where
K: TypeIndex,
{
self.0.get(index.type_index())
}
/// Returns number of elements in vector.
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns contained vector.
pub fn to_vec(&self) -> Vec<V> {
self.0.clone()
}
/// Returns an iterator over the underlying vector.
pub fn iter(&self) -> Iter<'_, V> {
self.0.iter()
}
/// Returns a mutable iterator over the underlying vector.
pub fn iter_mut(&mut self) -> IterMut<'_, V> {
self.0.iter_mut()
}
/// Creates a consuming iterator.
pub fn into_iter(self) -> IntoIter<V> {
self.0.into_iter()
}
/// Returns true if the underlying container is empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
/// The maximum number of validators `f` which may safely be faulty.
///
/// The total number of validators is `n = 3f + e` where `e in { 1, 2, 3 }`.
@@ -1603,7 +1698,7 @@ pub struct SessionInfo {
/// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
///
/// `SessionInfo::validators` will be limited to to `max_validators` when set.
pub validators: Vec<ValidatorId>,
pub validators: IndexedVec<ValidatorIndex, ValidatorId>,
/// Validators' authority discovery keys for the session in canonical ordering.
///
/// NOTE: The first `validators.len()` entries will match the corresponding validators in
@@ -1626,7 +1721,7 @@ pub struct SessionInfo {
/// Validators in shuffled ordering - these are the validator groups as produced
/// by the `Scheduler` module for the session and are typically referred to by
/// `GroupIndex`.
pub validator_groups: Vec<Vec<ValidatorIndex>>,
pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
/// The number of availability cores used by the protocol during this session.
pub n_cores: u32,
/// The zeroth delay tranche width.
@@ -1679,7 +1774,7 @@ pub struct OldV1SessionInfo {
/// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
///
/// `SessionInfo::validators` will be limited to to `max_validators` when set.
pub validators: Vec<ValidatorId>,
pub validators: IndexedVec<ValidatorIndex, ValidatorId>,
/// Validators' authority discovery keys for the session in canonical ordering.
///
/// NOTE: The first `validators.len()` entries will match the corresponding validators in
@@ -1702,7 +1797,7 @@ pub struct OldV1SessionInfo {
/// Validators in shuffled ordering - these are the validator groups as produced
/// by the `Scheduler` module for the session and are typically referred to by
/// `GroupIndex`.
pub validator_groups: Vec<Vec<ValidatorIndex>>,
pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
/// The number of availability cores used by the protocol during this session.
pub n_cores: u32,
/// The zeroth delay tranche width.
+8 -7
View File
@@ -25,9 +25,10 @@ use primitives::v2::{
collator_signature_payload, AvailabilityBitfield, BackedCandidate, CandidateCommitments,
CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt,
CompactStatement, CoreIndex, CoreOccupied, DisputeStatement, DisputeStatementSet, GroupIndex,
HeadData, Id as ParaId, InherentData as ParachainsInherentData, InvalidDisputeStatementKind,
PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned,
ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation,
HeadData, Id as ParaId, IndexedVec, InherentData as ParachainsInherentData,
InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext,
UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex,
ValidityAttestation,
};
use sp_core::{sr25519, H256};
use sp_runtime::{
@@ -65,7 +66,7 @@ fn byte32_slice_from(n: u32) -> [u8; 32] {
/// Paras inherent `enter` benchmark scenario builder.
pub(crate) struct BenchBuilder<T: paras_inherent::Config> {
/// Active validators. Validators should be declared prior to all other setup.
validators: Option<Vec<ValidatorId>>,
validators: Option<IndexedVec<ValidatorIndex, ValidatorId>>,
/// Starting block number; we expect it to get incremented on session setup.
block_number: T::BlockNumber,
/// Starting session; we expect it to get incremented on session setup.
@@ -410,7 +411,7 @@ impl<T: paras_inherent::Config> BenchBuilder<T> {
assert_eq!(<shared::Pallet<T>>::session_index(), target_session);
// We need to refetch validators since they have been shuffled.
let validators_shuffled: Vec<_> = session_info::Pallet::<T>::session_info(target_session)
let validators_shuffled = session_info::Pallet::<T>::session_info(target_session)
.unwrap()
.validators
.clone();
@@ -549,7 +550,7 @@ impl<T: paras_inherent::Config> BenchBuilder<T> {
.iter()
.take(*num_votes as usize)
.map(|val_idx| {
let public = validators.get(val_idx.0 as usize).unwrap();
let public = validators.get(*val_idx).unwrap();
let sig = UncheckedSigned::<CompactStatement>::benchmark_sign(
public,
CompactStatement::Valid(candidate_hash.clone()),
@@ -606,7 +607,7 @@ impl<T: paras_inherent::Config> BenchBuilder<T> {
self.dispute_statements.get(&seed).cloned().unwrap_or(validators.len() as u32);
let statements = (0..statements_len)
.map(|validator_index| {
let validator_public = &validators.get(validator_index as usize).expect("Test case is not borked. `ValidatorIndex` out of bounds of `ValidatorId`s.");
let validator_public = &validators.get(ValidatorIndex::from(validator_index)).expect("Test case is not borked. `ValidatorIndex` out of bounds of `ValidatorId`s.");
// We need dispute statements on each side. And we don't want a revert log
// so we make sure that we have a super majority with valid statements.
+1 -2
View File
@@ -985,8 +985,7 @@ impl<T: Config> Pallet<T> {
let mut importer = DisputeStateImporter::new(dispute_state, now);
for (i, (statement, validator_index, signature)) in set.statements.iter().enumerate() {
// assure the validator index and is present in the session info
let validator_public = match session_info.validators.get(validator_index.0 as usize)
{
let validator_public = match session_info.validators.get(*validator_index) {
None => {
filter.remove_index(i);
continue
@@ -257,7 +257,7 @@ where
let keys = losers
.into_iter()
.filter_map(|i| session_info.validators.get(i.0 as usize).cloned().map(|id| (i, id)))
.filter_map(|i| session_info.validators.get(i).cloned().map(|id| (i, id)))
.collect();
let unapplied = PendingSlashes { keys, kind };
<UnappliedSlashes<T>>::insert(session_index, candidate_hash, unapplied);
@@ -80,7 +80,7 @@ where
let session_index = crate::shared::Pallet::<T>::session_index();
let session_info = crate::session_info::Pallet::<T>::session_info(session_index);
let session_info = session_info.unwrap();
let validator_id = session_info.validators[0].clone();
let validator_id = session_info.validators.get(ValidatorIndex::from(0)).unwrap().clone();
let key = (PARACHAIN_KEY_TYPE_ID, validator_id.clone());
let key_owner_proof = pallet_session::historical::Pallet::<T>::prove(key).unwrap();
@@ -126,12 +126,12 @@ impl<T: Config> Pallet<T> {
let dispute_period = config.dispute_period;
let validators = notification.validators.clone();
let validators = notification.validators.clone().into();
let discovery_keys = <T as AuthorityDiscoveryConfig>::authorities();
let assignment_keys = AssignmentKeysUnsafe::<T>::get();
let active_set = <shared::Pallet<T>>::active_validator_indices();
let validator_groups = <scheduler::Pallet<T>>::validator_groups();
let validator_groups = <scheduler::Pallet<T>>::validator_groups().into();
let n_cores = <scheduler::Pallet<T>>::availability_cores().len() as u32;
let zeroth_delay_tranche_width = config.zeroth_delay_tranche_width;
let relay_vrf_modulo_samples = config.relay_vrf_modulo_samples;
@@ -201,7 +201,7 @@ fn session_info_active_subsets() {
});
let session = Sessions::<Test>::get(&1).unwrap();
assert_eq!(session.validators, validators);
assert_eq!(session.validators.to_vec(), validators);
assert_eq!(
session.discovery_keys,
take_active_subset_and_inactive(&active_set, &unscrambled_discovery),