Refactor BeaconChain and BeaconState genesis

Now it more easily supports using pre-build validator registries.
This commit is contained in:
Paul Hauner 2019-03-08 15:33:45 +11:00
parent 6efe2ad3e3
commit 4b21252ce4
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
4 changed files with 83 additions and 148 deletions

View File

@ -73,31 +73,18 @@ where
F: ForkChoice, F: ForkChoice,
{ {
/// Instantiate a new Beacon Chain, from genesis. /// Instantiate a new Beacon Chain, from genesis.
#[allow(clippy::too_many_arguments)] // Will be re-factored in the coming weeks. pub fn from_genesis(
pub fn genesis(
state_store: Arc<BeaconStateStore<T>>, state_store: Arc<BeaconStateStore<T>>,
block_store: Arc<BeaconBlockStore<T>>, block_store: Arc<BeaconBlockStore<T>>,
slot_clock: U, slot_clock: U,
genesis_time: u64, mut genesis_state: BeaconState,
latest_eth1_data: Eth1Data, genesis_block: BeaconBlock,
initial_validator_deposits: Vec<Deposit>,
spec: ChainSpec, spec: ChainSpec,
fork_choice: F, fork_choice: F,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
if initial_validator_deposits.is_empty() {
return Err(Error::InsufficientValidators);
}
let mut genesis_state = BeaconState::genesis(
genesis_time,
initial_validator_deposits,
latest_eth1_data,
&spec,
)?;
let state_root = genesis_state.canonical_root(); let state_root = genesis_state.canonical_root();
state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?;
let genesis_block = BeaconBlock::genesis(state_root, &spec);
let block_root = genesis_block.canonical_root(); let block_root = genesis_block.canonical_root();
block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?; block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?;

View File

@ -9,11 +9,12 @@ use fork_choice::BitwiseLMDGhost;
use log::debug; use log::debug;
use rayon::prelude::*; use rayon::prelude::*;
use slot_clock::TestingSlotClock; use slot_clock::TestingSlotClock;
use ssz::TreeHash;
use std::collections::HashSet; use std::collections::HashSet;
use std::iter::FromIterator; use std::iter::FromIterator;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use types::*; use types::{beacon_state::BeaconStateBuilder, *};
mod generate_deposits; mod generate_deposits;
mod load_deposits_from_file; mod load_deposits_from_file;
@ -67,15 +68,20 @@ impl BeaconChainHarness {
(keypairs, deposits) (keypairs, deposits)
}; };
let mut state_builder = BeaconStateBuilder::new(genesis_time, latest_eth1_data, &spec);
state_builder.process_initial_deposits(&initial_validator_deposits, &spec);
let genesis_state = state_builder.build(&spec).unwrap();
let state_root = Hash256::from_slice(&genesis_state.hash_tree_root());
let genesis_block = BeaconBlock::genesis(state_root, &spec);
// Create the Beacon Chain // Create the Beacon Chain
let beacon_chain = Arc::new( let beacon_chain = Arc::new(
BeaconChain::genesis( BeaconChain::from_genesis(
state_store.clone(), state_store.clone(),
block_store.clone(), block_store.clone(),
slot_clock, slot_clock,
genesis_time, genesis_state,
latest_eth1_data, genesis_block,
initial_validator_deposits,
spec.clone(), spec.clone(),
fork_choice, fork_choice,
) )

View File

@ -114,18 +114,13 @@ pub struct BeaconState {
impl BeaconState { impl BeaconState {
/// Produce the first state of the Beacon Chain. /// Produce the first state of the Beacon Chain.
pub fn genesis_without_validators( pub fn genesis(genesis_time: u64, latest_eth1_data: Eth1Data, spec: &ChainSpec) -> BeaconState {
genesis_time: u64,
latest_eth1_data: Eth1Data,
spec: &ChainSpec,
) -> Result<BeaconState, Error> {
debug!("Creating genesis state (without validator processing).");
let initial_crosslink = Crosslink { let initial_crosslink = Crosslink {
epoch: spec.genesis_epoch, epoch: spec.genesis_epoch,
crosslink_data_root: spec.zero_hash, crosslink_data_root: spec.zero_hash,
}; };
Ok(BeaconState { BeaconState {
/* /*
* Misc * Misc
*/ */
@ -188,19 +183,15 @@ impl BeaconState {
*/ */
cache_index_offset: 0, cache_index_offset: 0,
caches: vec![EpochCache::empty(); CACHED_EPOCHS], caches: vec![EpochCache::empty(); CACHED_EPOCHS],
}) }
} }
/// Produce the first state of the Beacon Chain. /// Produce the first state of the Beacon Chain.
pub fn genesis( pub fn process_initial_deposits(
genesis_time: u64, &mut self,
initial_validator_deposits: Vec<Deposit>, initial_validator_deposits: Vec<Deposit>,
latest_eth1_data: Eth1Data,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<BeaconState, Error> { ) -> Result<(), Error> {
let mut genesis_state =
BeaconState::genesis_without_validators(genesis_time, latest_eth1_data, spec)?;
debug!("Processing genesis deposits..."); debug!("Processing genesis deposits...");
let deposit_data = initial_validator_deposits let deposit_data = initial_validator_deposits
@ -208,29 +199,28 @@ impl BeaconState {
.map(|deposit| &deposit.deposit_data) .map(|deposit| &deposit.deposit_data)
.collect(); .collect();
genesis_state.process_deposits(deposit_data, spec); self.process_deposits(deposit_data, spec);
trace!("Processed genesis deposits."); trace!("Processed genesis deposits.");
for validator_index in 0..genesis_state.validator_registry.len() { for validator_index in 0..self.validator_registry.len() {
if genesis_state.get_effective_balance(validator_index, spec) >= spec.max_deposit_amount if self.get_effective_balance(validator_index, spec) >= spec.max_deposit_amount {
{ self.activate_validator(validator_index, true, spec);
genesis_state.activate_validator(validator_index, true, spec);
} }
} }
genesis_state.deposit_index = initial_validator_deposits.len() as u64; self.deposit_index = initial_validator_deposits.len() as u64;
let genesis_active_index_root = hash_tree_root(get_active_validator_indices( let genesis_active_index_root = hash_tree_root(get_active_validator_indices(
&genesis_state.validator_registry, &self.validator_registry,
spec.genesis_epoch, spec.genesis_epoch,
)); ));
genesis_state.latest_active_index_roots = self.latest_active_index_roots =
vec![genesis_active_index_root; spec.latest_active_index_roots_length]; vec![genesis_active_index_root; spec.latest_active_index_roots_length];
genesis_state.current_shuffling_seed =
genesis_state.generate_seed(spec.genesis_epoch, spec)?;
Ok(genesis_state) self.current_shuffling_seed = self.generate_seed(spec.genesis_epoch, spec)?;
Ok(())
} }
/// Returns the `hash_tree_root` of the state. /// Returns the `hash_tree_root` of the state.

View File

@ -1,5 +1,9 @@
use super::BeaconStateError;
use crate::*; use crate::*;
use crate::{validator_registry::get_active_validator_indices, *};
use bls::create_proof_of_possession; use bls::create_proof_of_possession;
use rayon::prelude::*;
use ssz::TreeHash;
/// Builds a `BeaconState` for use in testing or benchmarking. /// Builds a `BeaconState` for use in testing or benchmarking.
/// ///
@ -16,128 +20,73 @@ use bls::create_proof_of_possession;
/// Step (4) produces a clone of the BeaconState and doesn't consume the `BeaconStateBuilder` to /// Step (4) produces a clone of the BeaconState and doesn't consume the `BeaconStateBuilder` to
/// allow access to `self.keypairs` and `self.spec`. /// allow access to `self.keypairs` and `self.spec`.
pub struct BeaconStateBuilder { pub struct BeaconStateBuilder {
pub validator_count: usize, pub state: BeaconState,
pub state: Option<BeaconState>,
pub genesis_time: u64,
pub latest_eth1_data: Eth1Data,
pub spec: ChainSpec,
pub keypairs: Vec<Keypair>,
} }
impl BeaconStateBuilder { impl BeaconStateBuilder {
/// Create a new builder with the given number of validators. /// Create a new builder with the given number of validators.
pub fn new(validator_count: usize) -> Self { ///
let genesis_time = 10_000_000; /// Spec v0.4.0
pub fn new(genesis_time: u64, latest_eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
let latest_eth1_data = Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
};
let spec = ChainSpec::foundation();
Self { Self {
validator_count, state: BeaconState::genesis(genesis_time, latest_eth1_data, spec),
state: None,
genesis_time,
latest_eth1_data,
spec,
keypairs: vec![],
} }
} }
/// Produce the first state of the Beacon Chain.
///
/// Spec v0.4.0
pub fn process_initial_deposits(
&mut self,
initial_validator_deposits: &[Deposit],
spec: &ChainSpec,
) {
let deposit_data = initial_validator_deposits
.par_iter()
.map(|deposit| &deposit.deposit_data)
.collect();
self.state.process_deposits(deposit_data, spec);
for validator_index in 0..self.state.validator_registry.len() {
if self.state.get_effective_balance(validator_index, spec) >= spec.max_deposit_amount {
self.state.activate_validator(validator_index, true, spec);
}
}
self.state.deposit_index = initial_validator_deposits.len() as u64;
}
/// Builds a `BeaconState` using the `BeaconState::genesis(..)` function. /// Builds a `BeaconState` using the `BeaconState::genesis(..)` function.
/// ///
/// Each validator is assigned a unique, randomly-generated keypair and all /// Each validator is assigned a unique, randomly-generated keypair and all
/// proof-of-possessions are verified during genesis. /// proof-of-possessions are verified during genesis.
pub fn build(&mut self) -> Result<(), BeaconStateError> {
self.keypairs = (0..self.validator_count)
.collect::<Vec<usize>>()
.iter()
.map(|_| Keypair::random())
.collect();
let initial_validator_deposits = self
.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: self.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 state = BeaconState::genesis(
self.genesis_time,
initial_validator_deposits,
self.latest_eth1_data.clone(),
&self.spec,
)?;
self.state = Some(state);
Ok(())
}
/// Builds a `BeaconState` using the `BeaconState::genesis(..)` function, without supplying any
/// validators. Instead validators are added to the state post-genesis.
/// ///
/// One keypair is randomly generated and all validators are assigned this same keypair. /// Spec v0.4.0
/// Proof-of-possessions are not created (or validated). pub fn build(mut self, spec: &ChainSpec) -> Result<BeaconState, BeaconStateError> {
/// let genesis_active_index_root =
/// This function runs orders of magnitude faster than `Self::build()`, however it will be get_active_validator_indices(&self.state.validator_registry, spec.genesis_epoch)
/// erroneous for functions which use a validators public key as an identifier (e.g., .hash_tree_root();
/// deposits).
pub fn build_fast(&mut self) -> Result<(), BeaconStateError> {
let common_keypair = Keypair::random();
let mut validator_registry = Vec::with_capacity(self.validator_count); self.state.latest_active_index_roots = vec![
let mut validator_balances = Vec::with_capacity(self.validator_count); Hash256::from_slice(&genesis_active_index_root);
self.keypairs = Vec::with_capacity(self.validator_count); spec.latest_active_index_roots_length
];
for _ in 0..self.validator_count { self.state.current_shuffling_seed = self.state.generate_seed(spec.genesis_epoch, spec)?;
self.keypairs.push(common_keypair.clone());
validator_balances.push(32_000_000_000); Ok(self.state)
validator_registry.push(Validator {
pubkey: common_keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(),
activation_epoch: self.spec.genesis_epoch,
..Validator::default()
})
}
let state = BeaconState {
validator_registry,
validator_balances,
..BeaconState::genesis(
self.genesis_time,
vec![],
self.latest_eth1_data.clone(),
&self.spec,
)?
};
self.state = Some(state);
Ok(())
} }
/*
/// Sets the `BeaconState` to be in the last slot of the given epoch. /// Sets the `BeaconState` to be in the last slot of the given epoch.
/// ///
/// Sets all justification/finalization parameters to be be as "perfect" as possible (i.e., /// Sets all justification/finalization parameters to be be as "perfect" as possible (i.e.,
/// highest justified and finalized slots, full justification bitfield, etc). /// highest justified and finalized slots, full justification bitfield, etc).
pub fn teleport_to_end_of_epoch(&mut self, epoch: Epoch) { pub fn teleport_to_end_of_epoch(&mut self, epoch: Epoch, spec: &ChainSpec) {
let state = self.state.as_mut().expect("Genesis required"); let state = &mut self.state;
let slot = epoch.end_slot(self.spec.slots_per_epoch); let slot = epoch.end_slot(spec.slots_per_epoch);
state.slot = slot; state.slot = slot;
state.validator_registry_update_epoch = epoch - 1; state.validator_registry_update_epoch = epoch - 1;
@ -159,7 +108,7 @@ impl BeaconStateBuilder {
/// ///
/// These attestations should be fully conducive to justification and finalization. /// These attestations should be fully conducive to justification and finalization.
pub fn insert_attestations(&mut self) { pub fn insert_attestations(&mut self) {
let state = self.state.as_mut().expect("Genesis required"); let state = &mut self.state;
state state
.build_epoch_cache(RelativeEpoch::Previous, &self.spec) .build_epoch_cache(RelativeEpoch::Previous, &self.spec)
@ -198,8 +147,10 @@ impl BeaconStateBuilder {
pub fn cloned_state(&self) -> BeaconState { pub fn cloned_state(&self) -> BeaconState {
self.state.as_ref().expect("Genesis required").clone() self.state.as_ref().expect("Genesis required").clone()
} }
*/
} }
/*
/// Builds a valid PendingAttestation with full participation for some committee. /// Builds a valid PendingAttestation with full participation for some committee.
fn committee_to_pending_attestation( fn committee_to_pending_attestation(
state: &BeaconState, state: &BeaconState,
@ -261,3 +212,4 @@ fn committee_to_pending_attestation(
inclusion_slot: slot, inclusion_slot: slot,
} }
} }
*/