Merge pull request #114 from sigp/issue/105/genesis
Implement genesis/startup functions
This commit is contained in:
commit
6f4ffd8d98
@ -35,6 +35,7 @@ name = "lighthouse"
|
||||
members = [
|
||||
"beacon_chain/attestation_validation",
|
||||
"beacon_chain/chain",
|
||||
"beacon_chain/genesis",
|
||||
"beacon_chain/naive_fork_choice",
|
||||
"beacon_chain/spec",
|
||||
"beacon_chain/types",
|
||||
|
@ -12,8 +12,9 @@ use super::{Error, Invalid, Outcome};
|
||||
pub fn validate_attestation_justified_slot(
|
||||
data: &AttestationData,
|
||||
state: &BeaconState,
|
||||
epoch_length: u64,
|
||||
) -> Result<Outcome, Error> {
|
||||
let permissable_justified_slot = if data.slot >= state.latest_state_recalculation_slot {
|
||||
let permissable_justified_slot = if data.slot >= state.slot - (state.slot % epoch_length) {
|
||||
state.justified_slot
|
||||
} else {
|
||||
state.previous_justified_slot
|
||||
|
@ -19,7 +19,7 @@ where
|
||||
match state.latest_crosslinks.get(data.shard as usize) {
|
||||
None => reject!(Invalid::UnknownShard),
|
||||
Some(crosslink) => {
|
||||
let local_shard_block_hash = crosslink.shard_block_hash;
|
||||
let local_shard_block_hash = crosslink.shard_block_root;
|
||||
let shard_block_hash_is_permissable = {
|
||||
(local_shard_block_hash == data.latest_crosslink_hash)
|
||||
|| (local_shard_block_hash == data.shard_block_hash)
|
||||
|
@ -7,7 +7,9 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bls = { path = "../utils/bls" }
|
||||
db = { path = "../../lighthouse/db" }
|
||||
genesis = { path = "../genesis" }
|
||||
naive_fork_choice = { path = "../naive_fork_choice" }
|
||||
spec = { path = "../spec" }
|
||||
ssz = { path = "../utils/ssz" }
|
||||
types = { path = "../types" }
|
||||
validator_induction = { path = "../validator_induction" }
|
||||
|
@ -1,154 +0,0 @@
|
||||
use super::{ActiveState, ChainConfig, CrystallizedState};
|
||||
use types::ValidatorStatus;
|
||||
use validator_induction::ValidatorInductor;
|
||||
use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
ValidationAssignmentError(ValidatorAssignmentError),
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
impl From<ValidatorAssignmentError> for Error {
|
||||
fn from(e: ValidatorAssignmentError) -> Error {
|
||||
Error::ValidationAssignmentError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a new ChainHead with genesis parameters.
|
||||
///
|
||||
/// Used when syncing a chain from scratch.
|
||||
pub fn genesis_states(config: &ChainConfig) -> Result<(ActiveState, CrystallizedState), Error> {
|
||||
/*
|
||||
* Parse the ValidatorRegistrations into ValidatorRecords and induct them.
|
||||
*
|
||||
* Ignore any records which fail proof-of-possession or are invalid.
|
||||
*/
|
||||
let validators = {
|
||||
let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]);
|
||||
for registration in &config.initial_validators {
|
||||
let _ = inductor.induct(®istration, ValidatorStatus::Active);
|
||||
}
|
||||
inductor.to_vec()
|
||||
};
|
||||
|
||||
/*
|
||||
* Assign the validators to shards, using all zeros as the seed.
|
||||
*
|
||||
* Crystallizedstate stores two cycles, so we simply repeat the same assignment twice.
|
||||
*/
|
||||
let _shard_and_committee_for_slots = {
|
||||
let mut a = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?;
|
||||
let mut b = a.clone();
|
||||
a.append(&mut b);
|
||||
a
|
||||
};
|
||||
|
||||
// TODO: implement genesis for `BeaconState`
|
||||
// https://github.com/sigp/lighthouse/issues/99
|
||||
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate bls;
|
||||
extern crate validator_induction;
|
||||
|
||||
// TODO: implement genesis for `BeaconState`
|
||||
// https://github.com/sigp/lighthouse/issues/99
|
||||
//
|
||||
/*
|
||||
use self::bls::{create_proof_of_possession, Keypair};
|
||||
use super::*;
|
||||
use types::{Address, Hash256, ValidatorRegistration};
|
||||
|
||||
#[test]
|
||||
fn test_genesis_no_validators() {
|
||||
let config = ChainConfig::standard();
|
||||
let (act, cry) = genesis_states(&config).unwrap();
|
||||
|
||||
assert_eq!(cry.validator_set_change_slot, 0);
|
||||
assert_eq!(cry.validators.len(), 0);
|
||||
assert_eq!(cry.crosslinks.len(), config.shard_count as usize);
|
||||
for cl in cry.crosslinks {
|
||||
assert_eq!(cl.recently_changed, false);
|
||||
assert_eq!(cl.slot, 0);
|
||||
assert_eq!(cl.hash, Hash256::zero());
|
||||
}
|
||||
assert_eq!(cry.last_state_recalculation_slot, 0);
|
||||
assert_eq!(cry.last_finalized_slot, 0);
|
||||
assert_eq!(cry.last_justified_slot, 0);
|
||||
assert_eq!(cry.justified_streak, 0);
|
||||
assert_eq!(
|
||||
cry.shard_and_committee_for_slots.len(),
|
||||
(config.cycle_length as usize) * 2
|
||||
);
|
||||
assert_eq!(cry.deposits_penalized_in_period.len(), 0);
|
||||
assert_eq!(cry.validator_set_delta_hash_chain, Hash256::zero());
|
||||
assert_eq!(cry.pre_fork_version, INITIAL_FORK_VERSION);
|
||||
assert_eq!(cry.post_fork_version, INITIAL_FORK_VERSION);
|
||||
assert_eq!(cry.fork_slot_number, 0);
|
||||
|
||||
assert_eq!(act.pending_attestations.len(), 0);
|
||||
assert_eq!(act.pending_specials.len(), 0);
|
||||
assert_eq!(
|
||||
act.recent_block_hashes,
|
||||
vec![Hash256::zero(); config.cycle_length as usize]
|
||||
);
|
||||
assert_eq!(act.randao_mix, Hash256::zero());
|
||||
}
|
||||
|
||||
fn random_registration() -> ValidatorRegistration {
|
||||
let keypair = Keypair::random();
|
||||
ValidatorRegistration {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_shard: 0,
|
||||
withdrawal_address: Address::random(),
|
||||
randao_commitment: Hash256::random(),
|
||||
proof_of_possession: create_proof_of_possession(&keypair),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_genesis_valid_validators() {
|
||||
let mut config = ChainConfig::standard();
|
||||
let validator_count = 5;
|
||||
|
||||
for _ in 0..validator_count {
|
||||
config.initial_validators.push(random_registration());
|
||||
}
|
||||
|
||||
let (_, cry) = genesis_states(&config).unwrap();
|
||||
|
||||
assert_eq!(cry.validators.len(), validator_count);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_genesis_invalid_validators() {
|
||||
let mut config = ChainConfig::standard();
|
||||
let good_validator_count = 5;
|
||||
|
||||
for _ in 0..good_validator_count {
|
||||
config.initial_validators.push(random_registration());
|
||||
}
|
||||
|
||||
let mut bad_v = random_registration();
|
||||
let bad_kp = Keypair::random();
|
||||
bad_v.proof_of_possession = create_proof_of_possession(&bad_kp);
|
||||
config.initial_validators.push(bad_v);
|
||||
|
||||
let mut bad_v = random_registration();
|
||||
bad_v.withdrawal_shard = config.shard_count + 1;
|
||||
config.initial_validators.push(bad_v);
|
||||
|
||||
let (_, cry) = genesis_states(&config).unwrap();
|
||||
|
||||
assert!(
|
||||
config.initial_validators.len() != good_validator_count,
|
||||
"test is invalid"
|
||||
);
|
||||
assert_eq!(cry.validators.len(), good_validator_count);
|
||||
}
|
||||
*/
|
||||
}
|
@ -1,22 +1,24 @@
|
||||
extern crate db;
|
||||
extern crate naive_fork_choice;
|
||||
extern crate genesis;
|
||||
extern crate spec;
|
||||
extern crate ssz;
|
||||
extern crate types;
|
||||
extern crate validator_induction;
|
||||
extern crate validator_shuffling;
|
||||
|
||||
mod block_processing;
|
||||
mod genesis;
|
||||
mod maps;
|
||||
mod stores;
|
||||
|
||||
use db::ClientDB;
|
||||
use crate::genesis::{genesis_states, Error as GenesisError};
|
||||
use crate::maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError};
|
||||
use crate::stores::BeaconChainStore;
|
||||
use genesis::{genesis_beacon_state, GenesisError};
|
||||
use spec::ChainSpec;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use crate::stores::BeaconChainStore;
|
||||
use types::{ActiveState, AttesterMap, ChainConfig, CrystallizedState, Hash256, ProposerMap};
|
||||
use types::{AttesterMap, BeaconState, Hash256, ProposerMap};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BeaconChainError {
|
||||
@ -34,43 +36,46 @@ pub struct BeaconChain<T: ClientDB + Sized> {
|
||||
pub head_block_hashes: Vec<Hash256>,
|
||||
/// The index of the canonical block in `head_block_hashes`.
|
||||
pub canonical_head_block_hash: usize,
|
||||
/// A map where the value is an active state the the key is its hash.
|
||||
pub active_states: HashMap<Hash256, ActiveState>,
|
||||
/// A map where the value is crystallized state the the key is its hash.
|
||||
pub crystallized_states: HashMap<Hash256, CrystallizedState>,
|
||||
/// An in-memory map of root hash to beacon state.
|
||||
pub beacon_states: HashMap<Hash256, BeaconState>,
|
||||
/// A map of crystallized state to a proposer and attester map.
|
||||
pub attester_proposer_maps: HashMap<Hash256, (Arc<AttesterMap>, Arc<ProposerMap>)>,
|
||||
/// A collection of database stores used by the chain.
|
||||
pub store: BeaconChainStore<T>,
|
||||
/// The chain configuration.
|
||||
pub config: ChainConfig,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub fn new(store: BeaconChainStore<T>, config: ChainConfig) -> Result<Self, BeaconChainError> {
|
||||
if config.initial_validators.is_empty() {
|
||||
pub fn new(store: BeaconChainStore<T>, spec: ChainSpec) -> Result<Self, BeaconChainError> {
|
||||
if spec.initial_validators.is_empty() {
|
||||
return Err(BeaconChainError::InsufficientValidators);
|
||||
}
|
||||
|
||||
let (active_state, crystallized_state) = genesis_states(&config)?;
|
||||
/*
|
||||
* Generate and process the genesis state.
|
||||
*/
|
||||
let genesis_state = genesis_beacon_state(&spec)?;
|
||||
let mut beacon_states = HashMap::new();
|
||||
beacon_states.insert(genesis_state.canonical_root(), genesis_state.clone());
|
||||
|
||||
// TODO: implement genesis block
|
||||
// https://github.com/sigp/lighthouse/issues/105
|
||||
let canonical_latest_block_hash = Hash256::zero();
|
||||
|
||||
let head_block_hashes = vec![canonical_latest_block_hash];
|
||||
let canonical_head_block_hash = 0;
|
||||
let mut active_states = HashMap::new();
|
||||
let mut crystallized_states = HashMap::new();
|
||||
|
||||
let mut attester_proposer_maps = HashMap::new();
|
||||
|
||||
let (attester_map, proposer_map) = generate_attester_and_proposer_maps(
|
||||
&crystallized_state.shard_and_committee_for_slots,
|
||||
&genesis_state.shard_committees_at_slots,
|
||||
0,
|
||||
)?;
|
||||
|
||||
active_states.insert(canonical_latest_block_hash, active_state);
|
||||
crystallized_states.insert(canonical_latest_block_hash, crystallized_state);
|
||||
attester_proposer_maps.insert(
|
||||
canonical_latest_block_hash,
|
||||
(Arc::new(attester_map), Arc::new(proposer_map)),
|
||||
@ -80,11 +85,10 @@ where
|
||||
last_finalized_slot: 0,
|
||||
head_block_hashes,
|
||||
canonical_head_block_hash,
|
||||
active_states,
|
||||
crystallized_states,
|
||||
beacon_states,
|
||||
attester_proposer_maps,
|
||||
store,
|
||||
config,
|
||||
spec,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use types::{AttesterMap, ProposerMap, ShardAndCommittee};
|
||||
use types::{AttesterMap, ProposerMap, ShardCommittee};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterAndProposerMapError {
|
||||
NoShardAndCommitteeForSlot,
|
||||
NoShardCommitteeForSlot,
|
||||
NoAvailableProposer,
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ pub enum AttesterAndProposerMapError {
|
||||
///
|
||||
/// The attester map is used to optimise the lookup of a committee.
|
||||
pub fn generate_attester_and_proposer_maps(
|
||||
shard_and_committee_for_slots: &Vec<Vec<ShardAndCommittee>>,
|
||||
shard_and_committee_for_slots: &Vec<Vec<ShardCommittee>>,
|
||||
start_slot: u64,
|
||||
) -> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> {
|
||||
let mut attester_map = AttesterMap::new();
|
||||
@ -22,7 +22,7 @@ pub fn generate_attester_and_proposer_maps(
|
||||
let slot_number = (i as u64).saturating_add(start_slot);
|
||||
let first_committee = &slot
|
||||
.get(0)
|
||||
.ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)?
|
||||
.ok_or(AttesterAndProposerMapError::NoShardCommitteeForSlot)?
|
||||
.committee;
|
||||
let proposer_index = (slot_number as usize)
|
||||
.checked_rem(first_committee.len())
|
||||
@ -49,15 +49,15 @@ mod tests {
|
||||
slot_count: usize,
|
||||
sac_per_slot: usize,
|
||||
committee_size: usize,
|
||||
) -> Vec<Vec<ShardAndCommittee>> {
|
||||
) -> Vec<Vec<ShardCommittee>> {
|
||||
let mut shard = 0;
|
||||
let mut validator = 0;
|
||||
let mut cycle = vec![];
|
||||
|
||||
for _ in 0..slot_count {
|
||||
let mut slot: Vec<ShardAndCommittee> = vec![];
|
||||
let mut slot: Vec<ShardCommittee> = vec![];
|
||||
for _ in 0..sac_per_slot {
|
||||
let mut sac = ShardAndCommittee {
|
||||
let mut sac = ShardCommittee {
|
||||
shard: shard % shard_count,
|
||||
committee: vec![],
|
||||
};
|
||||
@ -79,7 +79,7 @@ mod tests {
|
||||
let result = generate_attester_and_proposer_maps(&sac, 0);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)
|
||||
Err(AttesterAndProposerMapError::NoShardCommitteeForSlot)
|
||||
);
|
||||
}
|
||||
|
||||
|
29
beacon_chain/chain/src/transition.rs
Normal file
29
beacon_chain/chain/src/transition.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use super::BeaconChain;
|
||||
use db::ClientDB;
|
||||
use state_transition::{extend_active_state, StateTransitionError};
|
||||
use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256};
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub(crate) fn transition_states(
|
||||
&self,
|
||||
act_state: &ActiveState,
|
||||
cry_state: &CrystallizedState,
|
||||
block: &BeaconBlock,
|
||||
block_hash: &Hash256,
|
||||
) -> Result<(ActiveState, Option<CrystallizedState>), StateTransitionError> {
|
||||
let state_recalc_distance = block
|
||||
.slot
|
||||
.checked_sub(cry_state.last_state_recalculation_slot)
|
||||
.ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?;
|
||||
|
||||
if state_recalc_distance >= u64::from(self.spec.epoch_length) {
|
||||
panic!("Not implemented!")
|
||||
} else {
|
||||
let new_act_state = extend_active_state(act_state, block, block_hash)?;
|
||||
Ok((new_act_state, None))
|
||||
}
|
||||
}
|
||||
}
|
13
beacon_chain/genesis/Cargo.toml
Normal file
13
beacon_chain/genesis/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "genesis"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../utils/bls" }
|
||||
spec = { path = "../spec" }
|
||||
ssz = { path = "../utils/ssz" }
|
||||
types = { path = "../types" }
|
||||
validator_induction = { path = "../validator_induction" }
|
||||
validator_shuffling = { path = "../validator_shuffling" }
|
49
beacon_chain/genesis/src/beacon_block.rs
Normal file
49
beacon_chain/genesis/src/beacon_block.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use bls::{Signature, BLS_AGG_SIG_BYTE_SIZE};
|
||||
use spec::ChainSpec;
|
||||
use ssz::{encode::encode_length, Decodable, LENGTH_BYTES};
|
||||
use types::{BeaconBlock, BeaconBlockBody};
|
||||
|
||||
/// Generate a genesis BeaconBlock.
|
||||
pub fn genesis_beacon_block(spec: &ChainSpec) -> BeaconBlock {
|
||||
BeaconBlock {
|
||||
slot: spec.initial_slot_number,
|
||||
parent_root: spec.zero_hash,
|
||||
state_root: spec.zero_hash,
|
||||
randao_reveal: spec.zero_hash,
|
||||
candidate_pow_receipt_root: spec.zero_hash,
|
||||
signature: genesis_signature(),
|
||||
body: BeaconBlockBody {
|
||||
proposer_slashings: vec![],
|
||||
casper_slashings: vec![],
|
||||
attestations: vec![],
|
||||
deposits: vec![],
|
||||
exits: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn genesis_signature() -> Signature {
|
||||
let mut bytes = encode_length(BLS_AGG_SIG_BYTE_SIZE, LENGTH_BYTES);
|
||||
bytes.append(&mut vec![0; BLS_AGG_SIG_BYTE_SIZE]);
|
||||
let (signature, _) = match Signature::ssz_decode(&bytes, 0) {
|
||||
Ok(sig) => sig,
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
signature
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// TODO: enhance these tests.
|
||||
// https://github.com/sigp/lighthouse/issues/117
|
||||
|
||||
#[test]
|
||||
fn test_genesis() {
|
||||
let spec = ChainSpec::foundation();
|
||||
|
||||
// This only checks that the function runs without panic.
|
||||
genesis_beacon_block(&spec);
|
||||
}
|
||||
}
|
135
beacon_chain/genesis/src/beacon_state.rs
Normal file
135
beacon_chain/genesis/src/beacon_state.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use spec::ChainSpec;
|
||||
use types::{BeaconState, CrosslinkRecord, ForkData, ValidatorStatus};
|
||||
use validator_induction::ValidatorInductor;
|
||||
use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
NoValidators,
|
||||
ValidationAssignmentError(ValidatorAssignmentError),
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
pub fn genesis_beacon_state(spec: &ChainSpec) -> Result<BeaconState, Error> {
|
||||
/*
|
||||
* Parse the ValidatorRegistrations into ValidatorRecords and induct them.
|
||||
*
|
||||
* Ignore any records which fail proof-of-possession or are invalid.
|
||||
*/
|
||||
let validators = {
|
||||
let mut inductor = ValidatorInductor::new(0, spec.shard_count, vec![]);
|
||||
for registration in &spec.initial_validators {
|
||||
let _ = inductor.induct(®istration, ValidatorStatus::Active);
|
||||
}
|
||||
inductor.to_vec()
|
||||
};
|
||||
|
||||
/*
|
||||
* Assign the validators to shards, using all zeros as the seed.
|
||||
*
|
||||
* Crystallizedstate stores two cycles, so we simply repeat the same assignment twice.
|
||||
*/
|
||||
let _shard_and_committee_for_slots = {
|
||||
let mut a = shard_and_committees_for_cycle(&[0; 32], &validators, 0, &spec)?;
|
||||
let mut b = a.clone();
|
||||
a.append(&mut b);
|
||||
a
|
||||
};
|
||||
|
||||
let initial_crosslink = CrosslinkRecord {
|
||||
slot: spec.initial_slot_number,
|
||||
shard_block_root: spec.zero_hash,
|
||||
};
|
||||
|
||||
Ok(BeaconState {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
slot: spec.initial_slot_number,
|
||||
genesis_time: spec.genesis_time,
|
||||
fork_data: ForkData {
|
||||
pre_fork_version: spec.initial_fork_version,
|
||||
post_fork_version: spec.initial_fork_version,
|
||||
fork_slot: spec.initial_slot_number,
|
||||
},
|
||||
/*
|
||||
* Validator registry
|
||||
*/
|
||||
validator_registry: validators,
|
||||
validator_registry_latest_change_slot: spec.initial_slot_number,
|
||||
validator_registry_exit_count: 0,
|
||||
validator_registry_delta_chain_tip: spec.zero_hash,
|
||||
/*
|
||||
* Randomness and committees
|
||||
*/
|
||||
randao_mix: spec.zero_hash,
|
||||
next_seed: spec.zero_hash,
|
||||
shard_committees_at_slots: vec![],
|
||||
persistent_committees: vec![],
|
||||
persistent_committee_reassignments: vec![],
|
||||
/*
|
||||
* Finality
|
||||
*/
|
||||
previous_justified_slot: spec.initial_slot_number,
|
||||
justified_slot: spec.initial_slot_number,
|
||||
justification_bitfield: 0,
|
||||
finalized_slot: spec.initial_slot_number,
|
||||
/*
|
||||
* Recent state
|
||||
*/
|
||||
latest_crosslinks: vec![initial_crosslink; spec.shard_count as usize],
|
||||
latest_block_roots: vec![spec.zero_hash; spec.epoch_length as usize],
|
||||
latest_penalized_exit_balances: vec![],
|
||||
latest_attestations: vec![],
|
||||
/*
|
||||
* PoW receipt root
|
||||
*/
|
||||
processed_pow_receipt_root: spec.processed_pow_receipt_root,
|
||||
candidate_pow_receipt_roots: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
impl From<ValidatorAssignmentError> for Error {
|
||||
fn from(e: ValidatorAssignmentError) -> Error {
|
||||
Error::ValidationAssignmentError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate bls;
|
||||
extern crate validator_induction;
|
||||
|
||||
use self::bls::{create_proof_of_possession, Keypair};
|
||||
use super::*;
|
||||
|
||||
// TODO: enhance these tests.
|
||||
// https://github.com/sigp/lighthouse/issues/117
|
||||
|
||||
#[test]
|
||||
fn test_genesis() {
|
||||
let spec = ChainSpec::foundation();
|
||||
|
||||
let state = genesis_beacon_state(&spec).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
state.validator_registry.len(),
|
||||
spec.initial_validators.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_genesis_bad_validator() {
|
||||
let mut spec = ChainSpec::foundation();
|
||||
|
||||
let random_kp = Keypair::random();
|
||||
spec.initial_validators[4].proof_of_possession = create_proof_of_possession(&random_kp);
|
||||
|
||||
let state = genesis_beacon_state(&spec).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
state.validator_registry.len(),
|
||||
spec.initial_validators.len() - 1
|
||||
);
|
||||
}
|
||||
}
|
10
beacon_chain/genesis/src/lib.rs
Normal file
10
beacon_chain/genesis/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
extern crate spec;
|
||||
extern crate types;
|
||||
extern crate validator_induction;
|
||||
extern crate validator_shuffling;
|
||||
|
||||
mod beacon_state;
|
||||
mod beacon_block;
|
||||
|
||||
pub use crate::beacon_block::genesis_beacon_block;
|
||||
pub use crate::beacon_state::{genesis_beacon_state, Error as GenesisError};
|
@ -5,4 +5,5 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../utils/bls" }
|
||||
types = { path = "../types" }
|
||||
|
@ -1,9 +1,13 @@
|
||||
use super::ChainSpec;
|
||||
use bls::{create_proof_of_possession, Keypair, PublicKey, SecretKey};
|
||||
|
||||
use types::{Address, Hash256};
|
||||
use types::{Address, Hash256, ValidatorRegistration};
|
||||
|
||||
impl ChainSpec {
|
||||
/// Returns a `ChainSpec` compatible with the specification from Ethereum Foundation.
|
||||
///
|
||||
/// Of course, the actual foundation specs are unknown at this point so these are just a rough
|
||||
/// estimate.
|
||||
pub fn foundation() -> Self {
|
||||
Self {
|
||||
/*
|
||||
@ -56,6 +60,59 @@ impl ChainSpec {
|
||||
max_attestations: 128,
|
||||
max_deposits: 16,
|
||||
max_exits: 16,
|
||||
/*
|
||||
* Intialization parameters
|
||||
*/
|
||||
initial_validators: initial_validators_for_testing(),
|
||||
genesis_time: 1544672897,
|
||||
processed_pow_receipt_root: Hash256::from("pow_root".as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a set of validator registrations to use with testing until the real chain starts.
|
||||
fn initial_validators_for_testing() -> Vec<ValidatorRegistration> {
|
||||
// Some dummy private keys to start with.
|
||||
let key_strings = vec![
|
||||
"jzjxxgjajfjrmgodszzsgqccmhnyvetcuxobhtynojtpdtbj",
|
||||
"gpeehcjudxdijzhjgirfuhahmnjutlchjmoffxmimbdejakd",
|
||||
"ntrrdwwebodokuwaclhoqreqyodngoyhurvesghjfxeswoaj",
|
||||
"cibmzkqrzdgdlrvqaxinwpvyhcgjkeysrsjkqtkcxvznsvth",
|
||||
"erqrfuahdwprsstkawggounxmihzhrvbhchcyiwtaypqcedr",
|
||||
];
|
||||
|
||||
let mut initial_validators = Vec::with_capacity(key_strings.len());
|
||||
for key_string in key_strings {
|
||||
let keypair = {
|
||||
let secret_key = match SecretKey::from_bytes(&key_string.as_bytes()) {
|
||||
Ok(key) => key,
|
||||
Err(_) => unreachable!(), // Keys are static and should not fail.
|
||||
};
|
||||
let public_key = PublicKey::from_secret_key(&secret_key);
|
||||
Keypair {
|
||||
sk: secret_key,
|
||||
pk: public_key,
|
||||
}
|
||||
};
|
||||
let validator_registration = ValidatorRegistration {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_shard: 0,
|
||||
withdrawal_address: Address::random(),
|
||||
randao_commitment: Hash256::random(),
|
||||
proof_of_possession: create_proof_of_possession(&keypair),
|
||||
};
|
||||
initial_validators.push(validator_registration);
|
||||
}
|
||||
|
||||
initial_validators
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_foundation_spec_can_be_constructed() {
|
||||
let _ = ChainSpec::foundation();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
extern crate bls;
|
||||
extern crate types;
|
||||
|
||||
mod foundation;
|
||||
|
||||
use types::{Address, Hash256};
|
||||
use types::{Address, Hash256, ValidatorRegistration};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ChainSpec {
|
||||
/*
|
||||
* Misc
|
||||
@ -55,4 +57,10 @@ pub struct ChainSpec {
|
||||
pub max_attestations: u64,
|
||||
pub max_deposits: u64,
|
||||
pub max_exits: u64,
|
||||
/*
|
||||
* Intialization parameters
|
||||
*/
|
||||
pub initial_validators: Vec<ValidatorRegistration>,
|
||||
pub genesis_time: u64,
|
||||
pub processed_pow_receipt_root: Hash256,
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::ssz::{Decodable, DecodeError, Encodable, SszStream};
|
||||
use super::{BeaconBlockBody, Hash256};
|
||||
use crate::test_utils::TestRandom;
|
||||
use bls::AggregateSignature;
|
||||
use bls::Signature;
|
||||
use rand::RngCore;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -11,7 +11,7 @@ pub struct BeaconBlock {
|
||||
pub state_root: Hash256,
|
||||
pub randao_reveal: Hash256,
|
||||
pub candidate_pow_receipt_root: Hash256,
|
||||
pub signature: AggregateSignature,
|
||||
pub signature: Signature,
|
||||
pub body: BeaconBlockBody,
|
||||
}
|
||||
|
||||
|
@ -2,33 +2,52 @@ use super::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord;
|
||||
use super::crosslink_record::CrosslinkRecord;
|
||||
use super::fork_data::ForkData;
|
||||
use super::pending_attestation_record::PendingAttestationRecord;
|
||||
use super::shard_and_committee::ShardAndCommittee;
|
||||
use super::shard_committee::ShardCommittee;
|
||||
use super::shard_reassignment_record::ShardReassignmentRecord;
|
||||
use super::validator_record::ValidatorRecord;
|
||||
use super::Hash256;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct BeaconState {
|
||||
// Misc
|
||||
pub slot: u64,
|
||||
pub genesis_time: u64,
|
||||
pub fork_data: ForkData,
|
||||
|
||||
// Validator registry
|
||||
pub validator_registry: Vec<ValidatorRecord>,
|
||||
pub validator_registry_latest_change_slot: u64,
|
||||
pub validator_registry_exit_count: u64,
|
||||
pub validator_registry_delta_chain_tip: Hash256,
|
||||
|
||||
// Randomness and committees
|
||||
pub randao_mix: Hash256,
|
||||
pub next_seed: Hash256,
|
||||
pub shard_and_committee_for_slots: Vec<Vec<ShardAndCommittee>>,
|
||||
pub shard_committees_at_slots: Vec<Vec<ShardCommittee>>,
|
||||
pub persistent_committees: Vec<Vec<u32>>,
|
||||
pub persistent_committee_reassignments: Vec<ShardReassignmentRecord>,
|
||||
|
||||
// Finality
|
||||
pub previous_justified_slot: u64,
|
||||
pub justified_slot: u64,
|
||||
pub justified_slot_bitfield: u64,
|
||||
pub justification_bitfield: u64,
|
||||
pub finalized_slot: u64,
|
||||
|
||||
// Recent state
|
||||
pub latest_crosslinks: Vec<CrosslinkRecord>,
|
||||
pub latest_state_recalculation_slot: u64,
|
||||
pub latest_block_hashes: Vec<Hash256>,
|
||||
pub latest_block_roots: Vec<Hash256>,
|
||||
pub latest_penalized_exit_balances: Vec<u64>,
|
||||
pub latest_attestations: Vec<PendingAttestationRecord>,
|
||||
|
||||
// PoW receipt root
|
||||
pub processed_pow_receipt_root: Hash256,
|
||||
pub candidate_pow_receipt_roots: Vec<CandidatePoWReceiptRootRecord>,
|
||||
pub genesis_time: u64,
|
||||
pub fork_data: ForkData,
|
||||
}
|
||||
|
||||
impl BeaconState {
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
// TODO: implement tree hashing.
|
||||
// https://github.com/sigp/lighthouse/issues/70
|
||||
Hash256::zero()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::Hash256;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CandidatePoWReceiptRootRecord {
|
||||
pub candidate_pow_receipt_root: Hash256,
|
||||
pub votes: u64,
|
||||
|
@ -3,7 +3,7 @@ use super::Hash256;
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CrosslinkRecord {
|
||||
pub slot: u64,
|
||||
pub shard_block_hash: Hash256,
|
||||
pub shard_block_root: Hash256,
|
||||
}
|
||||
|
||||
impl CrosslinkRecord {
|
||||
@ -11,7 +11,7 @@ impl CrosslinkRecord {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
slot: 0,
|
||||
shard_block_hash: Hash256::zero(),
|
||||
shard_block_root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::crosslink_record::CrosslinkRecord;
|
||||
use super::shard_and_committee::ShardAndCommittee;
|
||||
use super::shard_committee::ShardCommittee;
|
||||
use super::validator_record::ValidatorRecord;
|
||||
use super::Hash256;
|
||||
|
||||
@ -12,7 +12,7 @@ pub struct CrystallizedState {
|
||||
pub last_finalized_slot: u64,
|
||||
pub last_justified_slot: u64,
|
||||
pub justified_streak: u64,
|
||||
pub shard_and_committee_for_slots: Vec<Vec<ShardAndCommittee>>,
|
||||
pub shard_and_committee_for_slots: Vec<Vec<ShardCommittee>>,
|
||||
pub deposits_penalized_in_period: Vec<u32>,
|
||||
pub validator_set_delta_hash_chain: Hash256,
|
||||
pub pre_fork_version: u32,
|
||||
|
@ -24,7 +24,7 @@ pub mod fork_data;
|
||||
pub mod pending_attestation_record;
|
||||
pub mod proposal_signed_data;
|
||||
pub mod proposer_slashing;
|
||||
pub mod shard_and_committee;
|
||||
pub mod shard_committee;
|
||||
pub mod shard_reassignment_record;
|
||||
pub mod special_record;
|
||||
pub mod slashable_vote_data;
|
||||
@ -53,7 +53,7 @@ pub use crate::pending_attestation_record::PendingAttestationRecord;
|
||||
pub use crate::proposal_signed_data::ProposalSignedData;
|
||||
pub use crate::proposer_slashing::ProposerSlashing;
|
||||
pub use crate::slashable_vote_data::SlashableVoteData;
|
||||
pub use crate::shard_and_committee::ShardAndCommittee;
|
||||
pub use crate::shard_committee::ShardCommittee;
|
||||
pub use crate::special_record::{SpecialRecord, SpecialRecordKind};
|
||||
pub use crate::validator_record::{ValidatorRecord, ValidatorStatus};
|
||||
pub use crate::validator_registration::ValidatorRegistration;
|
||||
|
@ -1,10 +1,10 @@
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ShardAndCommittee {
|
||||
pub struct ShardCommittee {
|
||||
pub shard: u16,
|
||||
pub committee: Vec<usize>,
|
||||
}
|
||||
|
||||
impl ShardAndCommittee {
|
||||
impl ShardCommittee {
|
||||
/// Returns a new instance where the `shard_id` is zero and the
|
||||
/// committee is an empty vector.
|
||||
pub fn zero() -> Self {
|
||||
@ -21,7 +21,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_shard_and_committee_zero() {
|
||||
let s = ShardAndCommittee::zero();
|
||||
let s = ShardCommittee::zero();
|
||||
assert_eq!(s.shard, 0);
|
||||
assert_eq!(s.committee.len(), 0);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ShardReassignmentRecord {
|
||||
pub validator_index: u64,
|
||||
pub shard: u64,
|
||||
|
@ -29,7 +29,7 @@ impl convert::From<u8> for ValidatorStatus {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ValidatorRecord {
|
||||
pub pubkey: PublicKey,
|
||||
pub withdrawal_shard: u16,
|
||||
pub withdrawal_shard: u64,
|
||||
pub withdrawal_address: Address,
|
||||
pub randao_commitment: Hash256,
|
||||
pub randao_last_change: u64,
|
||||
|
@ -5,7 +5,7 @@ use bls::{create_proof_of_possession, Keypair, PublicKey, Signature};
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ValidatorRegistration {
|
||||
pub pubkey: PublicKey,
|
||||
pub withdrawal_shard: u16,
|
||||
pub withdrawal_shard: u64,
|
||||
pub withdrawal_address: Address,
|
||||
pub randao_commitment: Hash256,
|
||||
pub proof_of_possession: Signature,
|
||||
|
@ -7,7 +7,7 @@ pub const DEPOSIT_GWEI: u64 = 32_000_000_000;
|
||||
/// Inducts validators into a `CrystallizedState`.
|
||||
pub struct ValidatorInductor {
|
||||
pub current_slot: u64,
|
||||
pub shard_count: u16,
|
||||
pub shard_count: u64,
|
||||
validators: Vec<ValidatorRecord>,
|
||||
empty_validator_start: usize,
|
||||
}
|
||||
@ -19,7 +19,7 @@ pub enum ValidatorInductionError {
|
||||
}
|
||||
|
||||
impl ValidatorInductor {
|
||||
pub fn new(current_slot: u64, shard_count: u16, validators: Vec<ValidatorRecord>) -> Self {
|
||||
pub fn new(current_slot: u64, shard_count: u64, validators: Vec<ValidatorRecord>) -> Self {
|
||||
Self {
|
||||
current_slot,
|
||||
shard_count,
|
||||
|
@ -6,5 +6,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
honey-badger-split = { path = "../utils/honey-badger-split" }
|
||||
spec = { path = "../spec" }
|
||||
types = { path = "../types" }
|
||||
vec_shuffle = { path = "../utils/vec_shuffle" }
|
||||
|
@ -1,4 +1,5 @@
|
||||
extern crate honey_badger_split;
|
||||
extern crate spec;
|
||||
extern crate types;
|
||||
extern crate vec_shuffle;
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
use std::cmp::min;
|
||||
|
||||
use honey_badger_split::SplitExt;
|
||||
use types::{ChainConfig, ShardAndCommittee, ValidatorRecord, ValidatorStatus};
|
||||
use spec::ChainSpec;
|
||||
use types::{ShardCommittee, ValidatorRecord, ValidatorStatus};
|
||||
use vec_shuffle::{shuffle, ShuffleErr};
|
||||
|
||||
type DelegatedCycle = Vec<Vec<ShardAndCommittee>>;
|
||||
type DelegatedCycle = Vec<Vec<ShardCommittee>>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidatorAssignmentError {
|
||||
@ -20,10 +21,10 @@ pub fn shard_and_committees_for_cycle(
|
||||
seed: &[u8],
|
||||
validators: &[ValidatorRecord],
|
||||
crosslinking_shard_start: u16,
|
||||
config: &ChainConfig,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<DelegatedCycle, ValidatorAssignmentError> {
|
||||
let shuffled_validator_indices = {
|
||||
let mut validator_indices = validators
|
||||
let validator_indices = validators
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, validator)| {
|
||||
@ -36,15 +37,15 @@ pub fn shard_and_committees_for_cycle(
|
||||
.collect();
|
||||
shuffle(seed, validator_indices)?
|
||||
};
|
||||
let shard_indices: Vec<usize> = (0_usize..config.shard_count as usize).into_iter().collect();
|
||||
let shard_indices: Vec<usize> = (0_usize..spec.shard_count as usize).into_iter().collect();
|
||||
let crosslinking_shard_start = crosslinking_shard_start as usize;
|
||||
let cycle_length = config.cycle_length as usize;
|
||||
let min_committee_size = config.min_committee_size as usize;
|
||||
let epoch_length = spec.epoch_length as usize;
|
||||
let min_committee_size = spec.target_committee_size as usize;
|
||||
generate_cycle(
|
||||
&shuffled_validator_indices,
|
||||
&shard_indices,
|
||||
crosslinking_shard_start,
|
||||
cycle_length,
|
||||
epoch_length,
|
||||
min_committee_size,
|
||||
)
|
||||
}
|
||||
@ -54,29 +55,29 @@ fn generate_cycle(
|
||||
validator_indices: &[usize],
|
||||
shard_indices: &[usize],
|
||||
crosslinking_shard_start: usize,
|
||||
cycle_length: usize,
|
||||
epoch_length: usize,
|
||||
min_committee_size: usize,
|
||||
) -> Result<DelegatedCycle, ValidatorAssignmentError> {
|
||||
let validator_count = validator_indices.len();
|
||||
let shard_count = shard_indices.len();
|
||||
|
||||
if shard_count / cycle_length == 0 {
|
||||
if shard_count / epoch_length == 0 {
|
||||
return Err(ValidatorAssignmentError::TooFewShards);
|
||||
}
|
||||
|
||||
let (committees_per_slot, slots_per_committee) = {
|
||||
if validator_count >= cycle_length * min_committee_size {
|
||||
if validator_count >= epoch_length * min_committee_size {
|
||||
let committees_per_slot = min(
|
||||
validator_count / cycle_length / (min_committee_size * 2) + 1,
|
||||
shard_count / cycle_length,
|
||||
validator_count / epoch_length / (min_committee_size * 2) + 1,
|
||||
shard_count / epoch_length,
|
||||
);
|
||||
let slots_per_committee = 1;
|
||||
(committees_per_slot, slots_per_committee)
|
||||
} else {
|
||||
let committees_per_slot = 1;
|
||||
let mut slots_per_committee = 1;
|
||||
while (validator_count * slots_per_committee < cycle_length * min_committee_size)
|
||||
& (slots_per_committee < cycle_length)
|
||||
while (validator_count * slots_per_committee < epoch_length * min_committee_size)
|
||||
& (slots_per_committee < epoch_length)
|
||||
{
|
||||
slots_per_committee *= 2;
|
||||
}
|
||||
@ -85,7 +86,7 @@ fn generate_cycle(
|
||||
};
|
||||
|
||||
let cycle = validator_indices
|
||||
.honey_badger_split(cycle_length)
|
||||
.honey_badger_split(epoch_length)
|
||||
.enumerate()
|
||||
.map(|(i, slot_indices)| {
|
||||
let shard_start =
|
||||
@ -93,7 +94,7 @@ fn generate_cycle(
|
||||
slot_indices
|
||||
.honey_badger_split(committees_per_slot)
|
||||
.enumerate()
|
||||
.map(|(j, shard_indices)| ShardAndCommittee {
|
||||
.map(|(j, shard_indices)| ShardCommittee {
|
||||
shard: ((shard_start + j) % shard_count) as u16,
|
||||
committee: shard_indices.to_vec(),
|
||||
})
|
||||
@ -119,7 +120,7 @@ mod tests {
|
||||
validator_count: &usize,
|
||||
shard_count: &usize,
|
||||
crosslinking_shard_start: usize,
|
||||
cycle_length: usize,
|
||||
epoch_length: usize,
|
||||
min_committee_size: usize,
|
||||
) -> (
|
||||
Vec<usize>,
|
||||
@ -132,7 +133,7 @@ mod tests {
|
||||
&validator_indices,
|
||||
&shard_indices,
|
||||
crosslinking_shard_start,
|
||||
cycle_length,
|
||||
epoch_length,
|
||||
min_committee_size,
|
||||
);
|
||||
(validator_indices, shard_indices, result)
|
||||
@ -194,13 +195,13 @@ mod tests {
|
||||
let validator_count: usize = 100;
|
||||
let shard_count: usize = 20;
|
||||
let crosslinking_shard_start: usize = 0;
|
||||
let cycle_length: usize = 20;
|
||||
let epoch_length: usize = 20;
|
||||
let min_committee_size: usize = 10;
|
||||
let (validators, shards, result) = generate_cycle_helper(
|
||||
&validator_count,
|
||||
&shard_count,
|
||||
crosslinking_shard_start,
|
||||
cycle_length,
|
||||
epoch_length,
|
||||
min_committee_size,
|
||||
);
|
||||
let cycle = result.unwrap();
|
||||
@ -253,13 +254,13 @@ mod tests {
|
||||
let validator_count: usize = 523;
|
||||
let shard_count: usize = 31;
|
||||
let crosslinking_shard_start: usize = 0;
|
||||
let cycle_length: usize = 11;
|
||||
let epoch_length: usize = 11;
|
||||
let min_committee_size: usize = 5;
|
||||
let (validators, shards, result) = generate_cycle_helper(
|
||||
&validator_count,
|
||||
&shard_count,
|
||||
crosslinking_shard_start,
|
||||
cycle_length,
|
||||
epoch_length,
|
||||
min_committee_size,
|
||||
);
|
||||
let cycle = result.unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user