Add state helpers from #148
This commit is contained in:
parent
f92b9d618a
commit
6a4252b8c6
@ -1,5 +1,11 @@
|
||||
use super::{BeaconChain, ClientDB, SlotClock};
|
||||
use types::PublicKey;
|
||||
use types::{beacon_state::Error as BeaconStateError, PublicKey};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
SlotClockError,
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
@ -39,10 +45,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_proposer(&self, slot: u64) -> Option<usize> {
|
||||
let present_slot = self.present_slot()?;
|
||||
let state = self.state(present_slot).ok()?;
|
||||
state.get_beacon_proposer_index(slot, &self.spec)
|
||||
pub fn block_proposer(&self, slot: u64) -> Result<usize, Error> {
|
||||
// TODO: fix unwrap
|
||||
let present_slot = self.present_slot().unwrap();
|
||||
// TODO: fix unwrap
|
||||
let state = self.state(present_slot).unwrap();
|
||||
let index = state.get_beacon_proposer_index(slot, &self.spec)?;
|
||||
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
pub fn justified_slot(&self) -> u64 {
|
||||
@ -60,3 +70,9 @@ where
|
||||
Some(state.attestation_slot_and_shard_for_validator(validator_index, &self.spec))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for Error {
|
||||
fn from(e: BeaconStateError) -> Error {
|
||||
Error::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ where
|
||||
|
||||
let block_proposer_index = state
|
||||
.get_beacon_proposer_index(block.slot, &self.spec)
|
||||
.ok_or(Error::NoBlockProducer)?;
|
||||
.map_err(|_| Error::NoBlockProducer)?;
|
||||
let block_proposer = &state.validator_registry[block_proposer_index];
|
||||
|
||||
if verify_block_signature {
|
||||
@ -294,7 +294,7 @@ where
|
||||
);
|
||||
|
||||
if state.slot % self.spec.epoch_length == 0 {
|
||||
state.per_epoch_processing(&self.spec);
|
||||
state.per_epoch_processing(&self.spec).unwrap();
|
||||
}
|
||||
|
||||
Ok(state)
|
||||
|
@ -113,10 +113,7 @@ impl BeaconChainHarness {
|
||||
pub fn produce_block(&mut self) -> BeaconBlock {
|
||||
let present_slot = self.beacon_chain.present_slot().unwrap();
|
||||
|
||||
let proposer = self
|
||||
.beacon_chain
|
||||
.block_proposer(present_slot)
|
||||
.expect("Unable to determine proposer.");
|
||||
let proposer = self.beacon_chain.block_proposer(present_slot).unwrap();
|
||||
|
||||
self.validators[proposer].produce_block().unwrap()
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ where
|
||||
.ok_or_else(|| ProducerDutiesReaderError::UnknownValidator)?;
|
||||
|
||||
match self.beacon_chain.block_proposer(slot) {
|
||||
Some(proposer) if proposer == validator_index => Ok(true),
|
||||
Some(_) => Ok(false),
|
||||
None => Err(ProducerDutiesReaderError::UnknownEpoch),
|
||||
Ok(proposer) if proposer == validator_index => Ok(true),
|
||||
Ok(_) => Ok(false),
|
||||
Err(_) => Err(ProducerDutiesReaderError::UnknownEpoch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ use types::ChainSpec;
|
||||
|
||||
#[test]
|
||||
fn it_can_build_on_genesis_block() {
|
||||
let validator_count = 2;
|
||||
let mut harness = BeaconChainHarness::new(ChainSpec::foundation(), validator_count);
|
||||
let validator_count = 10;
|
||||
let spec = ChainSpec::foundation();
|
||||
let mut harness = BeaconChainHarness::new(spec, validator_count);
|
||||
|
||||
harness.advance_chain_with_block();
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ pub fn genesis_beacon_state(spec: &ChainSpec) -> Result<BeaconState, Error> {
|
||||
current_epoch_start_shard: spec.genesis_start_shard,
|
||||
previous_epoch_calculation_slot: spec.genesis_slot,
|
||||
current_epoch_calculation_slot: spec.genesis_slot,
|
||||
previous_epoch_randao_mix: spec.zero_hash,
|
||||
current_epoch_randao_mix: spec.zero_hash,
|
||||
previous_epoch_seed: spec.zero_hash,
|
||||
current_epoch_seed: spec.zero_hash,
|
||||
/*
|
||||
* Custody challenges
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@ use integer_sqrt::IntegerSquareRoot;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
UnableToDetermineProducer,
|
||||
NoBlockRoots,
|
||||
@ -303,8 +304,8 @@ impl BeaconState {
|
||||
|
||||
for slot in self.slot.saturating_sub(2 * spec.epoch_length)..self.slot {
|
||||
let crosslink_committees_at_slot = self
|
||||
.get_crosslink_committees_at_slot(slot)
|
||||
.ok_or_else(|| Error::UnableToGetCrosslinkCommittees)?;
|
||||
.get_crosslink_committees_at_slot(slot, spec)
|
||||
.map_err(|_| Error::UnableToGetCrosslinkCommittees)?;
|
||||
|
||||
for (crosslink_committee, shard) in crosslink_committees_at_slot {
|
||||
let shard = shard as u64;
|
||||
@ -454,7 +455,7 @@ impl BeaconState {
|
||||
};
|
||||
let proposer_index = self
|
||||
.get_beacon_proposer_index(inclusion_slot, spec)
|
||||
.ok_or_else(|| Error::UnableToDetermineProducer)?;
|
||||
.map_err(|_| Error::UnableToDetermineProducer)?;
|
||||
let base_reward = self.base_reward(proposer_index, base_reward_quotient, spec);
|
||||
safe_add_assign!(
|
||||
self.validator_balances[proposer_index],
|
||||
@ -467,8 +468,8 @@ impl BeaconState {
|
||||
*/
|
||||
for slot in self.slot.saturating_sub(2 * spec.epoch_length)..self.slot {
|
||||
let crosslink_committees_at_slot = self
|
||||
.get_crosslink_committees_at_slot(slot)
|
||||
.ok_or_else(|| Error::UnableToGetCrosslinkCommittees)?;
|
||||
.get_crosslink_committees_at_slot(slot, spec)
|
||||
.map_err(|_| Error::UnableToGetCrosslinkCommittees)?;
|
||||
|
||||
for (_crosslink_committee, shard) in crosslink_committees_at_slot {
|
||||
let shard = shard as u64;
|
||||
@ -514,7 +515,7 @@ impl BeaconState {
|
||||
*/
|
||||
self.previous_epoch_calculation_slot = self.current_epoch_calculation_slot;
|
||||
self.previous_epoch_start_shard = self.current_epoch_start_shard;
|
||||
self.previous_epoch_randao_mix = self.current_epoch_randao_mix;
|
||||
self.previous_epoch_seed = self.current_epoch_seed;
|
||||
|
||||
let should_update_validator_registy = if self.finalized_slot
|
||||
> self.validator_registry_update_slot
|
||||
@ -534,7 +535,7 @@ impl BeaconState {
|
||||
self.current_epoch_start_shard = (self.current_epoch_start_shard
|
||||
+ self.get_current_epoch_committee_count_per_slot(spec) as u64 * spec.epoch_length)
|
||||
% spec.shard_count;
|
||||
self.current_epoch_randao_mix = self.get_randao_mix(
|
||||
self.current_epoch_seed = self.get_randao_mix(
|
||||
self.current_epoch_calculation_slot
|
||||
.saturating_sub(spec.seed_lookahead),
|
||||
spec,
|
||||
@ -544,7 +545,7 @@ impl BeaconState {
|
||||
(self.slot - self.validator_registry_update_slot) / spec.epoch_length;
|
||||
if epochs_since_last_registry_change.is_power_of_two() {
|
||||
self.current_epoch_calculation_slot = self.slot;
|
||||
self.current_epoch_randao_mix = self.get_randao_mix(
|
||||
self.current_epoch_seed = self.get_randao_mix(
|
||||
self.current_epoch_calculation_slot
|
||||
.saturating_sub(spec.seed_lookahead),
|
||||
spec,
|
||||
@ -709,14 +710,6 @@ impl BeaconState {
|
||||
(slot - slot % spec.epoch_length) + spec.epoch_length + spec.entry_exit_delay
|
||||
}
|
||||
|
||||
fn get_current_epoch_committee_count_per_slot(&self, spec: &ChainSpec) -> usize {
|
||||
let current_active_validators = get_active_validator_indices(
|
||||
&self.validator_registry,
|
||||
self.current_epoch_calculation_slot,
|
||||
);
|
||||
self.get_committee_count_per_slot(current_active_validators.len(), spec)
|
||||
}
|
||||
|
||||
fn process_ejections(&self) {
|
||||
//TODO: stubbed out.
|
||||
}
|
||||
|
@ -20,6 +20,12 @@ pub use self::attestation_validation::Error as AttestationValidationError;
|
||||
pub use self::epoch_processing::Error as EpochProcessingError;
|
||||
pub use self::slot_processing::Error as SlotProcessingError;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
InvalidSlot,
|
||||
InsufficientNumberOfValidators,
|
||||
}
|
||||
|
||||
// Custody will not be added to the specs until Phase 1 (Sharding Phase) so dummy class used.
|
||||
type CustodyChallenge = usize;
|
||||
|
||||
@ -44,8 +50,8 @@ pub struct BeaconState {
|
||||
pub current_epoch_start_shard: u64,
|
||||
pub previous_epoch_calculation_slot: u64,
|
||||
pub current_epoch_calculation_slot: u64,
|
||||
pub previous_epoch_randao_mix: Hash256,
|
||||
pub current_epoch_randao_mix: Hash256,
|
||||
pub previous_epoch_seed: Hash256,
|
||||
pub current_epoch_seed: Hash256,
|
||||
|
||||
// Custody challenges
|
||||
pub custody_challenges: Vec<CustodyChallenge>,
|
||||
@ -90,8 +96,8 @@ impl Encodable for BeaconState {
|
||||
s.append(&self.current_epoch_start_shard);
|
||||
s.append(&self.previous_epoch_calculation_slot);
|
||||
s.append(&self.current_epoch_calculation_slot);
|
||||
s.append(&self.previous_epoch_randao_mix);
|
||||
s.append(&self.current_epoch_randao_mix);
|
||||
s.append(&self.previous_epoch_seed);
|
||||
s.append(&self.current_epoch_seed);
|
||||
s.append(&self.custody_challenges);
|
||||
s.append(&self.previous_justified_slot);
|
||||
s.append(&self.justified_slot);
|
||||
@ -123,8 +129,8 @@ impl Decodable for BeaconState {
|
||||
let (current_epoch_start_shard, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (previous_epoch_calculation_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (current_epoch_calculation_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (previous_epoch_randao_mix, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (current_epoch_randao_mix, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (previous_epoch_seed, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (current_epoch_seed, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (custody_challenges, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (previous_justified_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||
let (justified_slot, i) = <_>::ssz_decode(bytes, i)?;
|
||||
@ -154,8 +160,8 @@ impl Decodable for BeaconState {
|
||||
current_epoch_start_shard,
|
||||
previous_epoch_calculation_slot,
|
||||
current_epoch_calculation_slot,
|
||||
previous_epoch_randao_mix,
|
||||
current_epoch_randao_mix,
|
||||
previous_epoch_seed,
|
||||
current_epoch_seed,
|
||||
custody_challenges,
|
||||
previous_justified_slot,
|
||||
justified_slot,
|
||||
@ -191,8 +197,8 @@ impl TreeHash for BeaconState {
|
||||
result.append(&mut self.current_epoch_start_shard.hash_tree_root());
|
||||
result.append(&mut self.previous_epoch_calculation_slot.hash_tree_root());
|
||||
result.append(&mut self.current_epoch_calculation_slot.hash_tree_root());
|
||||
result.append(&mut self.previous_epoch_randao_mix.hash_tree_root());
|
||||
result.append(&mut self.current_epoch_randao_mix.hash_tree_root());
|
||||
result.append(&mut self.previous_epoch_seed.hash_tree_root());
|
||||
result.append(&mut self.current_epoch_seed.hash_tree_root());
|
||||
result.append(&mut self.custody_challenges.hash_tree_root());
|
||||
result.append(&mut self.previous_justified_slot.hash_tree_root());
|
||||
result.append(&mut self.justified_slot.hash_tree_root());
|
||||
@ -226,8 +232,8 @@ impl<T: RngCore> TestRandom<T> for BeaconState {
|
||||
current_epoch_start_shard: <_>::random_for_test(rng),
|
||||
previous_epoch_calculation_slot: <_>::random_for_test(rng),
|
||||
current_epoch_calculation_slot: <_>::random_for_test(rng),
|
||||
previous_epoch_randao_mix: <_>::random_for_test(rng),
|
||||
current_epoch_randao_mix: <_>::random_for_test(rng),
|
||||
previous_epoch_seed: <_>::random_for_test(rng),
|
||||
current_epoch_seed: <_>::random_for_test(rng),
|
||||
custody_challenges: <_>::random_for_test(rng),
|
||||
previous_justified_slot: <_>::random_for_test(rng),
|
||||
justified_slot: <_>::random_for_test(rng),
|
||||
|
@ -1,9 +1,15 @@
|
||||
use super::Error;
|
||||
use crate::{validator_registry::get_active_validator_indices, BeaconState, ChainSpec, Hash256};
|
||||
use honey_badger_split::SplitExt;
|
||||
use std::ops::Range;
|
||||
use vec_shuffle::shuffle;
|
||||
|
||||
type CrosslinkCommittee = (Vec<usize>, usize);
|
||||
type CrosslinkCommittees = Vec<CrosslinkCommittee>;
|
||||
// utility function pending this functionality being stabilized on the `Range` type.
|
||||
fn range_contains<T: PartialOrd>(range: &Range<T>, target: T) -> bool {
|
||||
range.start <= target && target < range.end
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl BeaconState {
|
||||
pub fn get_shuffling(&self, seed: Hash256, slot: u64, spec: &ChainSpec) -> Vec<Vec<usize>> {
|
||||
@ -20,7 +26,7 @@ impl BeaconState {
|
||||
shuffle(&seed, active_validator_indices).expect("Max validator count exceed!");
|
||||
|
||||
shuffled_active_validator_indices
|
||||
.honey_badger_split(committees_per_slot * spec.epoch_length as usize)
|
||||
.honey_badger_split((committees_per_slot * spec.epoch_length) as usize)
|
||||
.filter_map(|slice: &[usize]| Some(slice.to_vec()))
|
||||
.collect()
|
||||
}
|
||||
@ -29,19 +35,110 @@ impl BeaconState {
|
||||
&self,
|
||||
active_validator_count: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> usize {
|
||||
) -> u64 {
|
||||
std::cmp::max(
|
||||
1,
|
||||
std::cmp::min(
|
||||
spec.shard_count as usize / spec.epoch_length as usize,
|
||||
active_validator_count
|
||||
/ spec.epoch_length as usize
|
||||
/ spec.target_committee_size as usize,
|
||||
spec.shard_count / spec.epoch_length,
|
||||
active_validator_count as u64 / spec.epoch_length / spec.target_committee_size,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_crosslink_committees_at_slot(&self, _slot: u64) -> Option<CrosslinkCommittees> {
|
||||
Some(vec![(vec![0], 0)])
|
||||
/// Returns the start slot and end slot of the current epoch containing `self.slot`.
|
||||
fn get_current_epoch_boundaries(&self, epoch_length: u64) -> Range<u64> {
|
||||
let slot_in_epoch = self.slot % epoch_length;
|
||||
let start = self.slot - slot_in_epoch;
|
||||
let end = self.slot + (epoch_length - slot_in_epoch);
|
||||
start..end
|
||||
}
|
||||
|
||||
fn get_previous_epoch_committee_count_per_slot(
|
||||
&self,
|
||||
spec: &ChainSpec,
|
||||
/*
|
||||
shard_count: u64,
|
||||
epoch_length: u64,
|
||||
target_committee_size: u64,
|
||||
*/
|
||||
) -> u64 {
|
||||
let previous_active_validators = get_active_validator_indices(
|
||||
&self.validator_registry,
|
||||
self.previous_epoch_calculation_slot,
|
||||
);
|
||||
self.get_committee_count_per_slot(previous_active_validators.len(), spec) as u64
|
||||
}
|
||||
|
||||
pub fn get_current_epoch_committee_count_per_slot(&self, spec: &ChainSpec) -> u64 {
|
||||
let current_active_validators = get_active_validator_indices(
|
||||
&self.validator_registry,
|
||||
self.current_epoch_calculation_slot,
|
||||
);
|
||||
self.get_committee_count_per_slot(current_active_validators.len(), spec)
|
||||
}
|
||||
|
||||
pub fn get_crosslink_committees_at_slot(
|
||||
&self,
|
||||
slot: u64,
|
||||
spec: &ChainSpec,
|
||||
/*
|
||||
epoch_length: u64,
|
||||
shard_count: u64,
|
||||
target_committee_size: u64,
|
||||
*/
|
||||
) -> Result<Vec<(Vec<usize>, u64)>> {
|
||||
let current_epoch_range = self.get_current_epoch_boundaries(spec.epoch_length);
|
||||
if !range_contains(¤t_epoch_range, slot) {
|
||||
return Err(Error::InvalidSlot);
|
||||
}
|
||||
let state_epoch_slot = current_epoch_range.start;
|
||||
let offset = slot % spec.epoch_length;
|
||||
|
||||
let (committees_per_slot, shuffling, slot_start_shard) = if slot < state_epoch_slot {
|
||||
let committees_per_slot = self.get_previous_epoch_committee_count_per_slot(spec);
|
||||
let shuffling = self.get_shuffling(
|
||||
self.previous_epoch_seed,
|
||||
self.previous_epoch_calculation_slot,
|
||||
spec,
|
||||
);
|
||||
let slot_start_shard =
|
||||
(self.previous_epoch_start_shard + committees_per_slot * offset) % spec.shard_count;
|
||||
(committees_per_slot, shuffling, slot_start_shard)
|
||||
} else {
|
||||
let committees_per_slot = self.get_current_epoch_committee_count_per_slot(spec);
|
||||
let shuffling = self.get_shuffling(
|
||||
self.current_epoch_seed,
|
||||
self.current_epoch_calculation_slot,
|
||||
spec,
|
||||
);
|
||||
let slot_start_shard =
|
||||
(self.current_epoch_start_shard + committees_per_slot * offset) % spec.shard_count;
|
||||
(committees_per_slot, shuffling, slot_start_shard)
|
||||
};
|
||||
|
||||
let shard_range = slot_start_shard..;
|
||||
Ok(shuffling
|
||||
.into_iter()
|
||||
.skip((committees_per_slot * offset) as usize)
|
||||
.zip(shard_range.into_iter())
|
||||
.take(committees_per_slot as usize)
|
||||
.map(|(committees, shard_number)| (committees, shard_number % spec.shard_count))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// Returns the beacon proposer index for the `slot`.
|
||||
/// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned.
|
||||
pub fn get_beacon_proposer_index(&self, slot: u64, spec: &ChainSpec) -> Result<usize> {
|
||||
let committees = self.get_crosslink_committees_at_slot(slot, spec)?;
|
||||
committees
|
||||
.first()
|
||||
.ok_or(Error::InsufficientNumberOfValidators)
|
||||
.and_then(|(first_committee, _)| {
|
||||
let index = (slot as usize)
|
||||
.checked_rem(first_committee.len())
|
||||
.ok_or(Error::InsufficientNumberOfValidators)?;
|
||||
// NOTE: next index will not panic as we have already returned if this is the case
|
||||
Ok(first_committee[index])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ impl BeaconState {
|
||||
|
||||
let block_proposer = self
|
||||
.get_beacon_proposer_index(self.slot, spec)
|
||||
.ok_or_else(|| Error::UnableToDetermineProducer)?;
|
||||
.map_err(|_| Error::UnableToDetermineProducer)?;
|
||||
|
||||
self.validator_registry[block_proposer].proposer_slots += 1;
|
||||
self.latest_randao_mixes[(self.slot % spec.latest_randao_mixes_length) as usize] =
|
||||
@ -31,14 +31,6 @@ impl BeaconState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_beacon_proposer_index(&self, slot: u64, spec: &ChainSpec) -> Option<usize> {
|
||||
// TODO: this is a stub; implement it properly.
|
||||
//
|
||||
// https://github.com/sigp/lighthouse/pull/148/files
|
||||
let validator_count = self.validator_registry.len();
|
||||
Some((slot as usize) % validator_count)
|
||||
}
|
||||
|
||||
pub fn attestation_slot_and_shard_for_validator(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
|
Loading…
Reference in New Issue
Block a user