Merge branch 'master' into lighthouse-150

This commit is contained in:
thojest
2019-02-18 11:49:37 +01:00
15 changed files with 526 additions and 259 deletions

View File

@@ -5,15 +5,19 @@ use crate::{
PendingAttestation, PublicKey, Signature, Slot, Validator,
};
use bls::verify_proof_of_possession;
use fisher_yates_shuffle::shuffle;
use honey_badger_split::SplitExt;
use log::trace;
use rand::RngCore;
use serde_derive::Serialize;
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};
use swap_or_not_shuffle::get_permutated_index;
mod tests;
#[derive(Debug, PartialEq)]
pub enum BeaconStateError {
EpochOutOfBounds,
UnableToShuffle,
InsufficientRandaoMixes,
InsufficientValidators,
InsufficientBlockRoots,
@@ -201,7 +205,12 @@ impl BeaconState {
///
/// Spec v0.2.0
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch {
self.current_epoch(spec).saturating_sub(1_u64)
let current_epoch = self.current_epoch(&spec);
if current_epoch == spec.genesis_epoch {
current_epoch
} else {
current_epoch - 1
}
}
/// The epoch following `self.current_epoch()`.
@@ -249,23 +258,50 @@ impl BeaconState {
/// committee is itself a list of validator indices.
///
/// Spec v0.1
pub fn get_shuffling(&self, seed: Hash256, epoch: Epoch, spec: &ChainSpec) -> Vec<Vec<usize>> {
pub fn get_shuffling(
&self,
seed: Hash256,
epoch: Epoch,
spec: &ChainSpec,
) -> Option<Vec<Vec<usize>>> {
let active_validator_indices =
get_active_validator_indices(&self.validator_registry, epoch);
if active_validator_indices.is_empty() {
return None;
}
trace!(
"get_shuffling: active_validator_indices.len() == {}",
active_validator_indices.len()
);
let committees_per_epoch =
self.get_epoch_committee_count(active_validator_indices.len(), spec);
// TODO: check that Hash256::from(u64) matches 'int_to_bytes32'.
let seed = seed ^ Hash256::from(epoch.as_u64());
// TODO: fix `expect` assert.
let shuffled_active_validator_indices =
shuffle(&seed, active_validator_indices).expect("Max validator count exceed!");
trace!(
"get_shuffling: active_validator_indices.len() == {}, committees_per_epoch: {}",
active_validator_indices.len(),
committees_per_epoch
);
shuffled_active_validator_indices
.honey_badger_split(committees_per_epoch as usize)
.map(|slice: &[usize]| slice.to_vec())
.collect()
let mut shuffled_active_validator_indices = vec![0; active_validator_indices.len()];
for &i in &active_validator_indices {
let shuffled_i = get_permutated_index(
i,
active_validator_indices.len(),
&seed[..],
spec.shuffle_round_count,
)?;
shuffled_active_validator_indices[i] = active_validator_indices[shuffled_i]
}
Some(
shuffled_active_validator_indices
.honey_badger_split(committees_per_epoch as usize)
.map(|slice: &[usize]| slice.to_vec())
.collect(),
)
}
/// Return the number of committees in the previous epoch.
@@ -303,9 +339,17 @@ impl BeaconState {
+ 1;
let latest_index_root = current_epoch + spec.entry_exit_delay;
trace!(
"get_active_index_root: epoch: {}, earliest: {}, latest: {}",
epoch,
earliest_index_root,
latest_index_root
);
if (epoch >= earliest_index_root) & (epoch <= latest_index_root) {
Some(self.latest_index_roots[epoch.as_usize() % spec.latest_index_roots_length])
} else {
trace!("get_active_index_root: epoch out of range.");
None
}
}
@@ -350,29 +394,28 @@ impl BeaconState {
) -> Result<Vec<(Vec<usize>, u64)>, BeaconStateError> {
let epoch = slot.epoch(spec.epoch_length);
let current_epoch = self.current_epoch(spec);
let previous_epoch = if current_epoch == spec.genesis_epoch {
current_epoch
} else {
current_epoch.saturating_sub(1_u64)
};
let previous_epoch = self.previous_epoch(spec);
let next_epoch = self.next_epoch(spec);
let (committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard) =
if epoch == previous_epoch {
(
self.get_previous_epoch_committee_count(spec),
self.previous_epoch_seed,
self.previous_calculation_epoch,
self.previous_epoch_start_shard,
)
} else if epoch == current_epoch {
if epoch == current_epoch {
trace!("get_crosslink_committees_at_slot: current_epoch");
(
self.get_current_epoch_committee_count(spec),
self.current_epoch_seed,
self.current_calculation_epoch,
self.current_epoch_start_shard,
)
} else if epoch == previous_epoch {
trace!("get_crosslink_committees_at_slot: previous_epoch");
(
self.get_previous_epoch_committee_count(spec),
self.previous_epoch_seed,
self.previous_calculation_epoch,
self.previous_epoch_start_shard,
)
} else if epoch == next_epoch {
trace!("get_crosslink_committees_at_slot: next_epoch");
let current_committees_per_epoch = self.get_current_epoch_committee_count(spec);
let epochs_since_last_registry_update =
current_epoch - self.validator_registry_update_epoch;
@@ -401,12 +444,21 @@ impl BeaconState {
return Err(BeaconStateError::EpochOutOfBounds);
};
let shuffling = self.get_shuffling(seed, shuffling_epoch, spec);
let shuffling = self
.get_shuffling(seed, shuffling_epoch, spec)
.ok_or_else(|| BeaconStateError::UnableToShuffle)?;
let offset = slot.as_u64() % spec.epoch_length;
let committees_per_slot = committees_per_epoch / spec.epoch_length;
let slot_start_shard =
(shuffling_start_shard + committees_per_slot * offset) % spec.shard_count;
trace!(
"get_crosslink_committees_at_slot: committees_per_slot: {}, slot_start_shard: {}, seed: {}",
committees_per_slot,
slot_start_shard,
seed
);
let mut crosslinks_at_slot = vec![];
for i in 0..committees_per_slot {
let tuple = (
@@ -458,6 +510,11 @@ impl BeaconState {
spec: &ChainSpec,
) -> Result<usize, BeaconStateError> {
let committees = self.get_crosslink_committees_at_slot(slot, false, spec)?;
trace!(
"get_beacon_proposer_index: slot: {}, committees_count: {}",
slot,
committees.len()
);
committees
.first()
.ok_or(BeaconStateError::InsufficientValidators)
@@ -1064,33 +1121,3 @@ impl<T: RngCore> TestRandom<T> for BeaconState {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let bytes = ssz_encode(&original);
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
pub fn test_hash_tree_root() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let result = original.hash_tree_root();
assert_eq!(result.len(), 32);
// TODO: Add further tests
// https://github.com/sigp/lighthouse/issues/170
}
}

View File

@@ -0,0 +1,97 @@
#![cfg(test)]
use super::*;
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
use crate::{
beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput,
Eth1Data, Hash256, Keypair,
};
use bls::create_proof_of_possession;
use ssz::ssz_encode;
struct BeaconStateTestBuilder {
pub genesis_time: u64,
pub initial_validator_deposits: Vec<Deposit>,
pub latest_eth1_data: Eth1Data,
pub spec: ChainSpec,
pub keypairs: Vec<Keypair>,
}
impl BeaconStateTestBuilder {
pub fn with_random_validators(validator_count: usize) -> Self {
let genesis_time = 10_000_000;
let keypairs: Vec<Keypair> = (0..validator_count)
.collect::<Vec<usize>>()
.iter()
.map(|_| Keypair::random())
.collect();
let initial_validator_deposits = keypairs
.iter()
.map(|keypair| Deposit {
branch: vec![], // branch verification is not specified.
index: 0, // index verification is not specified.
deposit_data: DepositData {
amount: 32_000_000_000, // 32 ETH (in Gwei)
timestamp: genesis_time - 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
proof_of_possession: create_proof_of_possession(&keypair),
},
},
})
.collect();
let latest_eth1_data = Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
};
let spec = ChainSpec::foundation();
Self {
genesis_time,
initial_validator_deposits,
latest_eth1_data,
spec,
keypairs,
}
}
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
BeaconState::genesis(
self.genesis_time,
self.initial_validator_deposits.clone(),
self.latest_eth1_data.clone(),
&self.spec,
)
}
}
#[test]
pub fn can_produce_genesis_block() {
let builder = BeaconStateTestBuilder::with_random_validators(2);
builder.build().unwrap();
}
#[test]
pub fn test_ssz_round_trip() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let bytes = ssz_encode(&original);
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
pub fn test_hash_tree_root() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let result = original.hash_tree_root();
assert_eq!(result.len(), 32);
// TODO: Add further tests
// https://github.com/sigp/lighthouse/issues/170
}

View File

@@ -1,72 +0,0 @@
#[cfg(test)]
mod tests {
use crate::{
beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput,
Eth1Data, Hash256, Keypair,
};
use bls::create_proof_of_possession;
struct BeaconStateTestBuilder {
pub genesis_time: u64,
pub initial_validator_deposits: Vec<Deposit>,
pub latest_eth1_data: Eth1Data,
pub spec: ChainSpec,
pub keypairs: Vec<Keypair>,
}
impl BeaconStateTestBuilder {
pub fn with_random_validators(validator_count: usize) -> Self {
let genesis_time = 10_000_000;
let keypairs: Vec<Keypair> = (0..validator_count)
.collect::<Vec<usize>>()
.iter()
.map(|_| Keypair::random())
.collect();
let initial_validator_deposits = keypairs
.iter()
.map(|keypair| Deposit {
branch: vec![], // branch verification is not specified.
index: 0, // index verification is not specified.
deposit_data: DepositData {
amount: 32_000_000_000, // 32 ETH (in Gwei)
timestamp: genesis_time - 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
proof_of_possession: create_proof_of_possession(&keypair),
},
},
})
.collect();
let latest_eth1_data = Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
};
let spec = ChainSpec::foundation();
Self {
genesis_time,
initial_validator_deposits,
latest_eth1_data,
spec,
keypairs,
}
}
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
BeaconState::genesis(
self.genesis_time,
self.initial_validator_deposits.clone(),
self.latest_eth1_data.clone(),
&self.spec,
)
}
}
#[test]
pub fn can_produce_genesis_block() {
let builder = BeaconStateTestBuilder::with_random_validators(2);
builder.build().unwrap();
}
}

View File

@@ -1,7 +1,96 @@
use crate::{Address, ChainSpec, Epoch, Hash256, Signature, Slot};
use crate::{Address, Epoch, Hash256, Slot};
use bls::Signature;
const GWEI: u64 = 1_000_000_000;
/// Holds all the "constants" for a BeaconChain.
///
/// Spec v0.2.0
#[derive(PartialEq, Debug, Clone)]
pub struct ChainSpec {
/*
* Misc
*/
pub shard_count: u64,
pub target_committee_size: u64,
pub max_balance_churn_quotient: u64,
pub beacon_chain_shard_number: u64,
pub max_indices_per_slashable_vote: u64,
pub max_withdrawals_per_epoch: u64,
pub shuffle_round_count: u8,
/*
* Deposit contract
*/
pub deposit_contract_address: Address,
pub deposit_contract_tree_depth: u64,
/*
* Gwei values
*/
pub min_deposit_amount: u64,
pub max_deposit_amount: u64,
pub fork_choice_balance_increment: u64,
pub ejection_balance: u64,
/*
* Initial Values
*/
pub genesis_fork_version: u64,
pub genesis_slot: Slot,
pub genesis_epoch: Epoch,
pub genesis_start_shard: u64,
pub far_future_epoch: Epoch,
pub zero_hash: Hash256,
pub empty_signature: Signature,
pub bls_withdrawal_prefix_byte: u8,
/*
* Time parameters
*/
pub slot_duration: u64,
pub min_attestation_inclusion_delay: u64,
pub epoch_length: u64,
pub seed_lookahead: Epoch,
pub entry_exit_delay: u64,
pub eth1_data_voting_period: u64,
pub min_validator_withdrawal_epochs: Epoch,
/*
* State list lengths
*/
pub latest_block_roots_length: usize,
pub latest_randao_mixes_length: usize,
pub latest_index_roots_length: usize,
pub latest_penalized_exit_length: usize,
/*
* Reward and penalty quotients
*/
pub base_reward_quotient: u64,
pub whistleblower_reward_quotient: u64,
pub includer_reward_quotient: u64,
pub inactivity_penalty_quotient: u64,
/*
* Max operations per block
*/
pub max_proposer_slashings: u64,
pub max_attester_slashings: u64,
pub max_attestations: u64,
pub max_deposits: u64,
pub max_exits: u64,
/*
* Signature domains
*/
pub domain_deposit: u64,
pub domain_attestation: u64,
pub domain_proposal: u64,
pub domain_exit: u64,
pub domain_randao: u64,
}
impl ChainSpec {
/// Returns a `ChainSpec` compatible with the specification from Ethereum Foundation.
///
@@ -100,6 +189,26 @@ impl ChainSpec {
}
}
impl ChainSpec {
/// Returns a `ChainSpec` compatible with the specification suitable for 8 validators.
///
/// Spec v0.2.0
pub fn few_validators() -> Self {
let genesis_slot = Slot::new(2_u64.pow(19));
let epoch_length = 8;
let genesis_epoch = genesis_slot.epoch(epoch_length);
Self {
shard_count: 1,
target_committee_size: 1,
genesis_slot,
genesis_epoch,
epoch_length,
..ChainSpec::foundation()
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -7,8 +7,8 @@ pub mod attester_slashing;
pub mod beacon_block;
pub mod beacon_block_body;
pub mod beacon_state;
pub mod beacon_state_tests;
pub mod casper_slashing;
pub mod chain_spec;
pub mod crosslink;
pub mod deposit;
pub mod deposit_data;
@@ -29,7 +29,6 @@ pub mod slashable_vote_data;
pub mod slot_epoch_macros;
pub mod slot_epoch;
pub mod slot_height;
pub mod spec;
pub mod validator;
pub mod validator_registry;
pub mod validator_registry_delta_block;
@@ -45,6 +44,7 @@ pub use crate::beacon_block::BeaconBlock;
pub use crate::beacon_block_body::BeaconBlockBody;
pub use crate::beacon_state::BeaconState;
pub use crate::casper_slashing::CasperSlashing;
pub use crate::chain_spec::ChainSpec;
pub use crate::crosslink::Crosslink;
pub use crate::deposit::Deposit;
pub use crate::deposit_data::DepositData;
@@ -61,7 +61,6 @@ pub use crate::slashable_attestation::SlashableAttestation;
pub use crate::slashable_vote_data::SlashableVoteData;
pub use crate::slot_epoch::{Epoch, Slot};
pub use crate::slot_height::SlotHeight;
pub use crate::spec::ChainSpec;
pub use crate::validator::{StatusFlags as ValidatorStatusFlags, Validator};
pub use crate::validator_registry_delta_block::ValidatorRegistryDeltaBlock;

View File

@@ -1,92 +0,0 @@
mod foundation;
use crate::{Address, Epoch, Hash256, Slot};
use bls::Signature;
/// Holds all the "constants" for a BeaconChain.
///
/// Spec v0.2.0
#[derive(PartialEq, Debug, Clone)]
pub struct ChainSpec {
/*
* Misc
*/
pub shard_count: u64,
pub target_committee_size: u64,
pub max_balance_churn_quotient: u64,
pub beacon_chain_shard_number: u64,
pub max_indices_per_slashable_vote: u64,
pub max_withdrawals_per_epoch: u64,
pub shuffle_round_count: u64,
/*
* Deposit contract
*/
pub deposit_contract_address: Address,
pub deposit_contract_tree_depth: u64,
/*
* Gwei values
*/
pub min_deposit_amount: u64,
pub max_deposit_amount: u64,
pub fork_choice_balance_increment: u64,
pub ejection_balance: u64,
/*
* Initial Values
*/
pub genesis_fork_version: u64,
pub genesis_slot: Slot,
pub genesis_epoch: Epoch,
pub genesis_start_shard: u64,
pub far_future_epoch: Epoch,
pub zero_hash: Hash256,
pub empty_signature: Signature,
pub bls_withdrawal_prefix_byte: u8,
/*
* Time parameters
*/
pub slot_duration: u64,
pub min_attestation_inclusion_delay: u64,
pub epoch_length: u64,
pub seed_lookahead: Epoch,
pub entry_exit_delay: u64,
pub eth1_data_voting_period: u64,
pub min_validator_withdrawal_epochs: Epoch,
/*
* State list lengths
*/
pub latest_block_roots_length: usize,
pub latest_randao_mixes_length: usize,
pub latest_index_roots_length: usize,
pub latest_penalized_exit_length: usize,
/*
* Reward and penalty quotients
*/
pub base_reward_quotient: u64,
pub whistleblower_reward_quotient: u64,
pub includer_reward_quotient: u64,
pub inactivity_penalty_quotient: u64,
/*
* Max operations per block
*/
pub max_proposer_slashings: u64,
pub max_attester_slashings: u64,
pub max_attestations: u64,
pub max_deposits: u64,
pub max_exits: u64,
/*
* Signature domains
*/
pub domain_deposit: u64,
pub domain_attestation: u64,
pub domain_proposal: u64,
pub domain_exit: u64,
pub domain_randao: u64,
}