Merge pull request #140 from ralexstokes/add-active-validators-helpers
Add active validators helpers
This commit is contained in:
commit
8e0e57dc95
@ -101,15 +101,7 @@ fn initial_validators_for_testing() -> Vec<ValidatorRecord> {
|
|||||||
};
|
};
|
||||||
let validator_record = ValidatorRecord {
|
let validator_record = ValidatorRecord {
|
||||||
pubkey: keypair.pk.clone(),
|
pubkey: keypair.pk.clone(),
|
||||||
withdrawal_credentials: Hash256::zero(),
|
..Default::default()
|
||||||
randao_commitment: Hash256::zero(),
|
|
||||||
randao_layers: 0,
|
|
||||||
status: From::from(0),
|
|
||||||
latest_status_change_slot: 0,
|
|
||||||
exit_count: 0,
|
|
||||||
custody_commitment: Hash256::zero(),
|
|
||||||
latest_custody_reseed_slot: 0,
|
|
||||||
penultimate_custody_reseed_slot: 0,
|
|
||||||
};
|
};
|
||||||
initial_validators.push(validator_record);
|
initial_validators.push(validator_record);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ pub mod shard_reassignment_record;
|
|||||||
pub mod slashable_vote_data;
|
pub mod slashable_vote_data;
|
||||||
pub mod special_record;
|
pub mod special_record;
|
||||||
pub mod validator_record;
|
pub mod validator_record;
|
||||||
|
pub mod validator_registry;
|
||||||
|
|
||||||
pub mod readers;
|
pub mod readers;
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ pub use crate::proposer_slashing::ProposerSlashing;
|
|||||||
pub use crate::shard_committee::ShardCommittee;
|
pub use crate::shard_committee::ShardCommittee;
|
||||||
pub use crate::slashable_vote_data::SlashableVoteData;
|
pub use crate::slashable_vote_data::SlashableVoteData;
|
||||||
pub use crate::special_record::{SpecialRecord, SpecialRecordKind};
|
pub use crate::special_record::{SpecialRecord, SpecialRecordKind};
|
||||||
pub use crate::validator_record::{ValidatorRecord, ValidatorStatus};
|
pub use crate::validator_record::{StatusFlags as ValidatorStatusFlags, ValidatorRecord};
|
||||||
|
|
||||||
pub type Hash256 = H256;
|
pub type Hash256 = H256;
|
||||||
pub type Address = H160;
|
pub type Address = H160;
|
||||||
|
@ -3,29 +3,43 @@ use super::Hash256;
|
|||||||
use crate::test_utils::TestRandom;
|
use crate::test_utils::TestRandom;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use ssz::{Decodable, DecodeError, Encodable, SszStream};
|
use ssz::{Decodable, DecodeError, Encodable, SszStream};
|
||||||
use std::convert;
|
|
||||||
|
const STATUS_FLAG_INITIATED_EXIT: u8 = 1;
|
||||||
|
const STATUS_FLAG_WITHDRAWABLE: u8 = 2;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum ValidatorStatus {
|
pub enum StatusFlags {
|
||||||
PendingActivation,
|
InitiatedExit,
|
||||||
Active,
|
Withdrawable,
|
||||||
PendingExit,
|
|
||||||
PendingWithdraw,
|
|
||||||
Withdrawn,
|
|
||||||
Penalized,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl convert::From<u8> for ValidatorStatus {
|
struct StatusFlagsDecodeError;
|
||||||
fn from(status: u8) -> Self {
|
|
||||||
match status {
|
impl From<StatusFlagsDecodeError> for DecodeError {
|
||||||
0 => ValidatorStatus::PendingActivation,
|
fn from(_: StatusFlagsDecodeError) -> DecodeError {
|
||||||
1 => ValidatorStatus::Active,
|
DecodeError::Invalid
|
||||||
2 => ValidatorStatus::PendingExit,
|
}
|
||||||
3 => ValidatorStatus::PendingWithdraw,
|
}
|
||||||
5 => ValidatorStatus::Withdrawn,
|
|
||||||
127 => ValidatorStatus::Penalized,
|
/// Handles the serialization logic for the `status_flags` field of the `ValidatorRecord`.
|
||||||
_ => unreachable!(),
|
fn status_flag_to_byte(flag: Option<StatusFlags>) -> u8 {
|
||||||
|
if let Some(flag) = flag {
|
||||||
|
match flag {
|
||||||
|
StatusFlags::InitiatedExit => STATUS_FLAG_INITIATED_EXIT,
|
||||||
|
StatusFlags::Withdrawable => STATUS_FLAG_WITHDRAWABLE,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the deserialization logic for the `status_flags` field of the `ValidatorRecord`.
|
||||||
|
fn status_flag_from_byte(flag: u8) -> Result<Option<StatusFlags>, StatusFlagsDecodeError> {
|
||||||
|
match flag {
|
||||||
|
0 => Ok(None),
|
||||||
|
1 => Ok(Some(StatusFlags::InitiatedExit)),
|
||||||
|
2 => Ok(Some(StatusFlags::Withdrawable)),
|
||||||
|
_ => Err(StatusFlagsDecodeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,61 +49,49 @@ pub struct ValidatorRecord {
|
|||||||
pub withdrawal_credentials: Hash256,
|
pub withdrawal_credentials: Hash256,
|
||||||
pub randao_commitment: Hash256,
|
pub randao_commitment: Hash256,
|
||||||
pub randao_layers: u64,
|
pub randao_layers: u64,
|
||||||
pub status: ValidatorStatus,
|
pub activation_slot: u64,
|
||||||
pub latest_status_change_slot: u64,
|
pub exit_slot: u64,
|
||||||
|
pub withdrawal_slot: u64,
|
||||||
|
pub penalized_slot: u64,
|
||||||
pub exit_count: u64,
|
pub exit_count: u64,
|
||||||
|
pub status_flags: Option<StatusFlags>,
|
||||||
pub custody_commitment: Hash256,
|
pub custody_commitment: Hash256,
|
||||||
pub latest_custody_reseed_slot: u64,
|
pub latest_custody_reseed_slot: u64,
|
||||||
pub penultimate_custody_reseed_slot: u64,
|
pub penultimate_custody_reseed_slot: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorRecord {
|
impl ValidatorRecord {
|
||||||
pub fn status_is(&self, status: ValidatorStatus) -> bool {
|
/// This predicate indicates if the validator represented by this record is considered "active" at `slot`.
|
||||||
self.status == status
|
pub fn is_active_at(&self, slot: u64) -> bool {
|
||||||
|
self.activation_slot <= slot && slot < self.exit_slot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for ValidatorStatus {
|
impl Default for ValidatorRecord {
|
||||||
fn ssz_append(&self, s: &mut SszStream) {
|
/// Yields a "default" `ValidatorRecord`. Primarily used for testing.
|
||||||
let byte: u8 = match self {
|
fn default() -> Self {
|
||||||
ValidatorStatus::PendingActivation => 0,
|
Self {
|
||||||
ValidatorStatus::Active => 1,
|
pubkey: PublicKey::default(),
|
||||||
ValidatorStatus::PendingExit => 2,
|
withdrawal_credentials: Hash256::default(),
|
||||||
ValidatorStatus::PendingWithdraw => 3,
|
randao_commitment: Hash256::default(),
|
||||||
ValidatorStatus::Withdrawn => 5,
|
randao_layers: 0,
|
||||||
ValidatorStatus::Penalized => 127,
|
activation_slot: std::u64::MAX,
|
||||||
};
|
exit_slot: std::u64::MAX,
|
||||||
s.append(&byte);
|
withdrawal_slot: std::u64::MAX,
|
||||||
|
penalized_slot: std::u64::MAX,
|
||||||
|
exit_count: 0,
|
||||||
|
status_flags: None,
|
||||||
|
custody_commitment: Hash256::default(),
|
||||||
|
latest_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
|
||||||
|
penultimate_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decodable for ValidatorStatus {
|
impl<T: RngCore> TestRandom<T> for StatusFlags {
|
||||||
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
|
|
||||||
let (byte, i) = u8::ssz_decode(bytes, i)?;
|
|
||||||
let status = match byte {
|
|
||||||
0 => ValidatorStatus::PendingActivation,
|
|
||||||
1 => ValidatorStatus::Active,
|
|
||||||
2 => ValidatorStatus::PendingExit,
|
|
||||||
3 => ValidatorStatus::PendingWithdraw,
|
|
||||||
5 => ValidatorStatus::Withdrawn,
|
|
||||||
127 => ValidatorStatus::Penalized,
|
|
||||||
_ => return Err(DecodeError::Invalid),
|
|
||||||
};
|
|
||||||
Ok((status, i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: RngCore> TestRandom<T> for ValidatorStatus {
|
|
||||||
fn random_for_test(rng: &mut T) -> Self {
|
fn random_for_test(rng: &mut T) -> Self {
|
||||||
let options = vec![
|
let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable];
|
||||||
ValidatorStatus::PendingActivation,
|
options[(rng.next_u32() as usize) % options.len()].clone()
|
||||||
ValidatorStatus::Active,
|
|
||||||
ValidatorStatus::PendingExit,
|
|
||||||
ValidatorStatus::PendingWithdraw,
|
|
||||||
ValidatorStatus::Withdrawn,
|
|
||||||
ValidatorStatus::Penalized,
|
|
||||||
];
|
|
||||||
options[(rng.next_u32() as usize) % options.len()]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,9 +101,12 @@ impl Encodable for ValidatorRecord {
|
|||||||
s.append(&self.withdrawal_credentials);
|
s.append(&self.withdrawal_credentials);
|
||||||
s.append(&self.randao_commitment);
|
s.append(&self.randao_commitment);
|
||||||
s.append(&self.randao_layers);
|
s.append(&self.randao_layers);
|
||||||
s.append(&self.status);
|
s.append(&self.activation_slot);
|
||||||
s.append(&self.latest_status_change_slot);
|
s.append(&self.exit_slot);
|
||||||
|
s.append(&self.withdrawal_slot);
|
||||||
|
s.append(&self.penalized_slot);
|
||||||
s.append(&self.exit_count);
|
s.append(&self.exit_count);
|
||||||
|
s.append(&status_flag_to_byte(self.status_flags));
|
||||||
s.append(&self.custody_commitment);
|
s.append(&self.custody_commitment);
|
||||||
s.append(&self.latest_custody_reseed_slot);
|
s.append(&self.latest_custody_reseed_slot);
|
||||||
s.append(&self.penultimate_custody_reseed_slot);
|
s.append(&self.penultimate_custody_reseed_slot);
|
||||||
@ -114,22 +119,30 @@ impl Decodable for ValidatorRecord {
|
|||||||
let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
|
let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?;
|
let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (randao_layers, i) = <_>::ssz_decode(bytes, i)?;
|
let (randao_layers, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (status, i) = <_>::ssz_decode(bytes, i)?;
|
let (activation_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (latest_status_change_slot, i) = <_>::ssz_decode(bytes, i)?;
|
let (exit_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
|
let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
|
let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
|
let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
|
let (status_flags_byte, i): (u8, usize) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?;
|
let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
|
let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
|
let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||||
|
|
||||||
|
let status_flags = status_flag_from_byte(status_flags_byte)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Self {
|
Self {
|
||||||
pubkey,
|
pubkey,
|
||||||
withdrawal_credentials,
|
withdrawal_credentials,
|
||||||
randao_commitment,
|
randao_commitment,
|
||||||
randao_layers,
|
randao_layers,
|
||||||
status,
|
activation_slot,
|
||||||
latest_status_change_slot,
|
exit_slot,
|
||||||
|
withdrawal_slot,
|
||||||
|
penalized_slot,
|
||||||
exit_count,
|
exit_count,
|
||||||
|
status_flags,
|
||||||
custody_commitment,
|
custody_commitment,
|
||||||
latest_custody_reseed_slot,
|
latest_custody_reseed_slot,
|
||||||
penultimate_custody_reseed_slot,
|
penultimate_custody_reseed_slot,
|
||||||
@ -146,12 +159,12 @@ impl<T: RngCore> TestRandom<T> for ValidatorRecord {
|
|||||||
withdrawal_credentials: <_>::random_for_test(rng),
|
withdrawal_credentials: <_>::random_for_test(rng),
|
||||||
randao_commitment: <_>::random_for_test(rng),
|
randao_commitment: <_>::random_for_test(rng),
|
||||||
randao_layers: <_>::random_for_test(rng),
|
randao_layers: <_>::random_for_test(rng),
|
||||||
status: <_>::random_for_test(rng),
|
|
||||||
latest_status_change_slot: <_>::random_for_test(rng),
|
|
||||||
exit_count: <_>::random_for_test(rng),
|
exit_count: <_>::random_for_test(rng),
|
||||||
|
status_flags: Some(<_>::random_for_test(rng)),
|
||||||
custody_commitment: <_>::random_for_test(rng),
|
custody_commitment: <_>::random_for_test(rng),
|
||||||
latest_custody_reseed_slot: <_>::random_for_test(rng),
|
latest_custody_reseed_slot: <_>::random_for_test(rng),
|
||||||
penultimate_custody_reseed_slot: <_>::random_for_test(rng),
|
penultimate_custody_reseed_slot: <_>::random_for_test(rng),
|
||||||
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,13 +187,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_validator_status_ssz_round_trip() {
|
fn test_validator_can_be_active() {
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
let original = ValidatorStatus::random_for_test(&mut rng);
|
let mut validator = ValidatorRecord::random_for_test(&mut rng);
|
||||||
|
|
||||||
let bytes = ssz_encode(&original);
|
let activation_slot = u64::random_for_test(&mut rng);
|
||||||
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
|
let exit_slot = activation_slot + 234;
|
||||||
|
|
||||||
assert_eq!(original, decoded);
|
validator.activation_slot = activation_slot;
|
||||||
|
validator.exit_slot = exit_slot;
|
||||||
|
|
||||||
|
for slot in (activation_slot - 100)..(exit_slot + 100) {
|
||||||
|
if slot < activation_slot {
|
||||||
|
assert!(!validator.is_active_at(slot));
|
||||||
|
} else if slot >= exit_slot {
|
||||||
|
assert!(!validator.is_active_at(slot));
|
||||||
|
} else {
|
||||||
|
assert!(validator.is_active_at(slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
171
beacon_chain/types/src/validator_registry.rs
Normal file
171
beacon_chain/types/src/validator_registry.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/// Contains logic to manipulate a `&[ValidatorRecord]`.
|
||||||
|
/// For now, we avoid defining a newtype and just have flat functions here.
|
||||||
|
use super::validator_record::*;
|
||||||
|
|
||||||
|
/// Given an indexed sequence of `validators`, return the indices corresponding to validators that are active at `slot`.
|
||||||
|
pub fn get_active_validator_indices(validators: &[ValidatorRecord], slot: u64) -> Vec<usize> {
|
||||||
|
validators
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, validator)| {
|
||||||
|
if validator.is_active_at(slot) {
|
||||||
|
Some(index)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_get_empty_active_validator_indices() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
|
||||||
|
let validators = vec![];
|
||||||
|
let some_slot = u64::random_for_test(&mut rng);
|
||||||
|
let indices = get_active_validator_indices(&validators, some_slot);
|
||||||
|
assert_eq!(indices, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_get_no_active_validator_indices() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
let mut validators = vec![];
|
||||||
|
let count_validators = 10;
|
||||||
|
for _ in 0..count_validators {
|
||||||
|
validators.push(ValidatorRecord::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
let some_slot = u64::random_for_test(&mut rng);
|
||||||
|
let indices = get_active_validator_indices(&validators, some_slot);
|
||||||
|
assert_eq!(indices, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_get_all_active_validator_indices() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
let count_validators = 10;
|
||||||
|
let some_slot = u64::random_for_test(&mut rng);
|
||||||
|
|
||||||
|
let mut validators = (0..count_validators)
|
||||||
|
.into_iter()
|
||||||
|
.map(|_| {
|
||||||
|
let mut validator = ValidatorRecord::default();
|
||||||
|
|
||||||
|
let activation_offset = u64::random_for_test(&mut rng);
|
||||||
|
let exit_offset = u64::random_for_test(&mut rng);
|
||||||
|
|
||||||
|
validator.activation_slot = some_slot.checked_sub(activation_offset).unwrap_or(0);
|
||||||
|
validator.exit_slot = some_slot.checked_add(exit_offset).unwrap_or(std::u64::MAX);
|
||||||
|
|
||||||
|
validator
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// test boundary condition by ensuring that at least one validator in the list just activated
|
||||||
|
if let Some(validator) = validators.get_mut(0) {
|
||||||
|
validator.activation_slot = some_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
let indices = get_active_validator_indices(&validators, some_slot);
|
||||||
|
assert_eq!(
|
||||||
|
indices,
|
||||||
|
(0..count_validators).into_iter().collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_validators_to_default_entry_exit(validators: &mut [ValidatorRecord]) {
|
||||||
|
for validator in validators.iter_mut() {
|
||||||
|
validator.activation_slot = std::u64::MAX;
|
||||||
|
validator.exit_slot = std::u64::MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets all `validators` to be active as of some slot prior to `slot`. returns the activation slot.
|
||||||
|
fn set_validators_to_activated(validators: &mut [ValidatorRecord], slot: u64) -> u64 {
|
||||||
|
let activation_slot = slot - 10;
|
||||||
|
for validator in validators.iter_mut() {
|
||||||
|
validator.activation_slot = activation_slot;
|
||||||
|
}
|
||||||
|
activation_slot
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets all `validators` to be exited as of some slot before `slot`.
|
||||||
|
fn set_validators_to_exited(
|
||||||
|
validators: &mut [ValidatorRecord],
|
||||||
|
slot: u64,
|
||||||
|
activation_slot: u64,
|
||||||
|
) {
|
||||||
|
assert!(activation_slot < slot);
|
||||||
|
let mut exit_slot = activation_slot + 10;
|
||||||
|
while exit_slot >= slot {
|
||||||
|
exit_slot -= 1;
|
||||||
|
}
|
||||||
|
assert!(activation_slot < exit_slot && exit_slot < slot);
|
||||||
|
|
||||||
|
for validator in validators.iter_mut() {
|
||||||
|
validator.exit_slot = exit_slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_get_some_active_validator_indices() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
const COUNT_PARTITIONS: usize = 3;
|
||||||
|
const COUNT_VALIDATORS: usize = 3 * COUNT_PARTITIONS;
|
||||||
|
let some_slot: u64 = u64::random_for_test(&mut rng);
|
||||||
|
|
||||||
|
let mut validators = (0..COUNT_VALIDATORS)
|
||||||
|
.into_iter()
|
||||||
|
.map(|_| {
|
||||||
|
let mut validator = ValidatorRecord::default();
|
||||||
|
|
||||||
|
let activation_offset = u64::random_for_test(&mut rng);
|
||||||
|
let exit_offset = u64::random_for_test(&mut rng);
|
||||||
|
|
||||||
|
validator.activation_slot = some_slot.checked_sub(activation_offset).unwrap_or(0);
|
||||||
|
validator.exit_slot = some_slot.checked_add(exit_offset).unwrap_or(std::u64::MAX);
|
||||||
|
|
||||||
|
validator
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// we partition the set into partitions based on lifecycle:
|
||||||
|
for (i, chunk) in validators.chunks_exact_mut(COUNT_PARTITIONS).enumerate() {
|
||||||
|
match i {
|
||||||
|
0 => {
|
||||||
|
// 1. not activated (Default::default())
|
||||||
|
set_validators_to_default_entry_exit(chunk);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// 2. activated, but not exited
|
||||||
|
set_validators_to_activated(chunk, some_slot);
|
||||||
|
// test boundary condition by ensuring that at least one validator in the list just activated
|
||||||
|
if let Some(validator) = chunk.get_mut(0) {
|
||||||
|
validator.activation_slot = some_slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// 3. exited
|
||||||
|
let activation_slot = set_validators_to_activated(chunk, some_slot);
|
||||||
|
set_validators_to_exited(chunk, some_slot, activation_slot);
|
||||||
|
// test boundary condition by ensuring that at least one validator in the list just exited
|
||||||
|
if let Some(validator) = chunk.get_mut(0) {
|
||||||
|
validator.exit_slot = some_slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(
|
||||||
|
"constants local to this test not in sync with generation of test case"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let indices = get_active_validator_indices(&validators, some_slot);
|
||||||
|
assert_eq!(indices, vec![3, 4, 5]);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
use super::SecretKey;
|
use super::SecretKey;
|
||||||
use bls_aggregates::PublicKey as RawPublicKey;
|
use bls_aggregates::PublicKey as RawPublicKey;
|
||||||
use ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream};
|
use ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream};
|
||||||
|
use std::default;
|
||||||
|
|
||||||
/// A single BLS signature.
|
/// A single BLS signature.
|
||||||
///
|
///
|
||||||
@ -20,6 +21,13 @@ impl PublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl default::Default for PublicKey {
|
||||||
|
fn default() -> Self {
|
||||||
|
let secret_key = SecretKey::random();
|
||||||
|
PublicKey::from_secret_key(&secret_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Encodable for PublicKey {
|
impl Encodable for PublicKey {
|
||||||
fn ssz_append(&self, s: &mut SszStream) {
|
fn ssz_append(&self, s: &mut SszStream) {
|
||||||
s.append_vec(&self.0.as_bytes());
|
s.append_vec(&self.0.as_bytes());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use bls::verify_proof_of_possession;
|
use bls::verify_proof_of_possession;
|
||||||
use spec::ChainSpec;
|
use spec::ChainSpec;
|
||||||
use types::{BeaconState, Deposit, ValidatorRecord, ValidatorStatus};
|
use types::{BeaconState, Deposit, ValidatorRecord};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum ValidatorInductionError {
|
pub enum ValidatorInductionError {
|
||||||
@ -43,13 +43,10 @@ pub fn process_deposit(
|
|||||||
pubkey: deposit_input.pubkey.clone(),
|
pubkey: deposit_input.pubkey.clone(),
|
||||||
withdrawal_credentials: deposit_input.withdrawal_credentials,
|
withdrawal_credentials: deposit_input.withdrawal_credentials,
|
||||||
randao_commitment: deposit_input.randao_commitment,
|
randao_commitment: deposit_input.randao_commitment,
|
||||||
randao_layers: 0,
|
|
||||||
status: ValidatorStatus::PendingActivation,
|
|
||||||
latest_status_change_slot: state.validator_registry_latest_change_slot,
|
|
||||||
exit_count: 0,
|
|
||||||
custody_commitment: deposit_input.custody_commitment,
|
custody_commitment: deposit_input.custody_commitment,
|
||||||
latest_custody_reseed_slot: 0,
|
latest_custody_reseed_slot: 0,
|
||||||
penultimate_custody_reseed_slot: 0,
|
penultimate_custody_reseed_slot: 0,
|
||||||
|
..std::default::Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
match min_empty_validator_index(state, spec) {
|
match min_empty_validator_index(state, spec) {
|
||||||
@ -68,13 +65,11 @@ pub fn process_deposit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min_empty_validator_index(state: &BeaconState, spec: &ChainSpec) -> Option<usize> {
|
// NOTE: this has been modified from the spec to get tests working
|
||||||
|
// this function is no longer used in the latest spec so this is simply a transition step
|
||||||
|
fn min_empty_validator_index(state: &BeaconState, _spec: &ChainSpec) -> Option<usize> {
|
||||||
for i in 0..state.validator_registry.len() {
|
for i in 0..state.validator_registry.len() {
|
||||||
if state.validator_balances[i] == 0
|
if state.validator_balances[i] == 0 {
|
||||||
&& state.validator_registry[i].latest_status_change_slot
|
|
||||||
+ spec.zero_balance_validator_ttl
|
|
||||||
<= state.slot
|
|
||||||
{
|
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,8 +182,7 @@ mod tests {
|
|||||||
let mut state = BeaconState::default();
|
let mut state = BeaconState::default();
|
||||||
let spec = ChainSpec::foundation();
|
let spec = ChainSpec::foundation();
|
||||||
|
|
||||||
let mut validator = get_validator();
|
let validator = get_validator();
|
||||||
validator.latest_status_change_slot = 0;
|
|
||||||
state.validator_registry.push(validator);
|
state.validator_registry.push(validator);
|
||||||
state.validator_balances.push(0);
|
state.validator_balances.push(0);
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ use std::cmp::min;
|
|||||||
|
|
||||||
use honey_badger_split::SplitExt;
|
use honey_badger_split::SplitExt;
|
||||||
use spec::ChainSpec;
|
use spec::ChainSpec;
|
||||||
use types::{ShardCommittee, ValidatorRecord, ValidatorStatus};
|
use types::validator_registry::get_active_validator_indices;
|
||||||
|
use types::{ShardCommittee, ValidatorRecord};
|
||||||
use vec_shuffle::{shuffle, ShuffleErr};
|
use vec_shuffle::{shuffle, ShuffleErr};
|
||||||
|
|
||||||
type DelegatedCycle = Vec<Vec<ShardCommittee>>;
|
type DelegatedCycle = Vec<Vec<ShardCommittee>>;
|
||||||
@ -24,17 +25,7 @@ pub fn shard_and_committees_for_cycle(
|
|||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<DelegatedCycle, ValidatorAssignmentError> {
|
) -> Result<DelegatedCycle, ValidatorAssignmentError> {
|
||||||
let shuffled_validator_indices = {
|
let shuffled_validator_indices = {
|
||||||
let validator_indices = validators
|
let validator_indices = get_active_validator_indices(validators, 0);
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, validator)| {
|
|
||||||
if validator.status_is(ValidatorStatus::Active) {
|
|
||||||
Some(i)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
shuffle(seed, validator_indices)?
|
shuffle(seed, validator_indices)?
|
||||||
};
|
};
|
||||||
let shard_indices: Vec<usize> = (0_usize..spec.shard_count as usize).into_iter().collect();
|
let shard_indices: Vec<usize> = (0_usize..spec.shard_count as usize).into_iter().collect();
|
||||||
|
Loading…
Reference in New Issue
Block a user