Add state helpers from #148

This commit is contained in:
Paul Hauner 2019-01-28 19:12:20 +11:00
parent f92b9d618a
commit 6a4252b8c6
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
10 changed files with 167 additions and 65 deletions

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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),
}
}
}

View File

@ -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();
}

View File

@ -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
*/

View File

@ -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.
}

View File

@ -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),

View File

@ -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(&current_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])
})
}
}

View File

@ -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,