Configurable maximum validators (#2586)

* guide: max_validators

* guide: new approach

* restrict validators based on configurable max

* add some tests

* fix wasm build (Vec)

* clean up warnings

* add logging

* set validator indices in tests as well

* fix common tests

* Update runtime/parachains/src/util.rs

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

Co-authored-by: Andronik Ordian <write@reusable.software>
This commit is contained in:
Robert Habermeier
2021-03-09 12:38:55 -06:00
committed by GitHub
parent 30e4a67f0c
commit 8b4f46d2df
17 changed files with 300 additions and 83 deletions
+160 -4
View File
@@ -19,12 +19,17 @@
//! To avoid cyclic dependencies, it is important that this module is not
//! dependent on any of the other modules.
use primitives::v1::SessionIndex;
use primitives::v1::{SessionIndex, ValidatorId, ValidatorIndex};
use frame_support::{
decl_storage, decl_module, decl_error,
weights::Weight,
};
use crate::initializer::SessionChangeNotification;
use sp_std::vec::Vec;
use rand::{SeedableRng, seq::SliceRandom};
use rand_chacha::ChaCha20Rng;
use crate::configuration::HostConfiguration;
pub trait Config: frame_system::Config { }
@@ -37,6 +42,12 @@ decl_storage! {
trait Store for Module<T: Config> as ParasShared {
/// The current session index.
CurrentSessionIndex get(fn session_index): SessionIndex;
/// All the validators actively participating in parachain consensus.
/// Indices are into the broader validator set.
ActiveValidatorIndices get(fn active_validator_indices): Vec<ValidatorIndex>;
/// The parachain attestation keys of the validators actively participating in parachain consensus.
/// This should be the same length as `ActiveValidatorIndices`.
ActiveValidatorKeys get(fn active_validator_keys): Vec<ValidatorId>;
}
}
@@ -63,8 +74,35 @@ impl<T: Config> Module<T> {
/// Called by the initializer to note that a new session has started.
///
/// Returns the list of outgoing paras from the actions queue.
pub(crate) fn initializer_on_new_session(notification: &SessionChangeNotification<T::BlockNumber>) {
CurrentSessionIndex::set(notification.session_index);
pub(crate) fn initializer_on_new_session(
session_index: SessionIndex,
random_seed: [u8; 32],
new_config: &HostConfiguration<T::BlockNumber>,
all_validators: Vec<ValidatorId>,
) -> Vec<ValidatorId> {
CurrentSessionIndex::set(session_index);
let mut rng: ChaCha20Rng = SeedableRng::from_seed(random_seed);
let mut shuffled_indices: Vec<_> = (0..all_validators.len())
.enumerate()
.map(|(i, _)| ValidatorIndex(i as _))
.collect();
shuffled_indices.shuffle(&mut rng);
if let Some(max) = new_config.max_validators {
shuffled_indices.truncate(max as usize);
}
let active_validator_keys = crate::util::take_active_subset(
&shuffled_indices,
&all_validators,
);
ActiveValidatorIndices::set(shuffled_indices);
ActiveValidatorKeys::set(active_validator_keys.clone());
active_validator_keys
}
/// Return the session index that should be used for any future scheduled changes.
@@ -76,4 +114,122 @@ impl<T: Config> Module<T> {
pub(crate) fn set_session_index(index: SessionIndex) {
CurrentSessionIndex::set(index);
}
#[cfg(test)]
pub(crate) fn set_active_validators(active: Vec<ValidatorId>) {
ActiveValidatorIndices::set(
(0..active.len()).map(|i| ValidatorIndex(i as _)).collect()
);
ActiveValidatorKeys::set(active);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::configuration::HostConfiguration;
use crate::mock::{new_test_ext, MockGenesisConfig, Shared};
use keyring::Sr25519Keyring;
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
val_ids.iter().map(|v| v.public().into()).collect()
}
#[test]
fn sets_and_shuffles_validators() {
let validators = vec![
Sr25519Keyring::Alice,
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
Sr25519Keyring::Dave,
Sr25519Keyring::Ferdie,
];
let mut config = HostConfiguration::default();
config.max_validators = None;
let pubkeys = validator_pubkeys(&validators);
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let validators = Shared::initializer_on_new_session(
1,
[1; 32],
&config,
pubkeys,
);
assert_eq!(
validators,
validator_pubkeys(&[
Sr25519Keyring::Ferdie,
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
Sr25519Keyring::Dave,
Sr25519Keyring::Alice,
])
);
assert_eq!(
Shared::active_validator_keys(),
validators,
);
assert_eq!(
Shared::active_validator_indices(),
vec![
ValidatorIndex(4),
ValidatorIndex(1),
ValidatorIndex(2),
ValidatorIndex(3),
ValidatorIndex(0),
]
);
});
}
#[test]
fn sets_truncates_and_shuffles_validators() {
let validators = vec![
Sr25519Keyring::Alice,
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
Sr25519Keyring::Dave,
Sr25519Keyring::Ferdie,
];
let mut config = HostConfiguration::default();
config.max_validators = Some(2);
let pubkeys = validator_pubkeys(&validators);
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
let validators = Shared::initializer_on_new_session(
1,
[1; 32],
&config,
pubkeys,
);
assert_eq!(
validators,
validator_pubkeys(&[
Sr25519Keyring::Ferdie,
Sr25519Keyring::Bob,
])
);
assert_eq!(
Shared::active_validator_keys(),
validators,
);
assert_eq!(
Shared::active_validator_indices(),
vec![
ValidatorIndex(4),
ValidatorIndex(1),
]
);
});
}
}