Merge pull request #114 from sigp/issue/105/genesis

Implement genesis/startup functions
This commit is contained in:
Paul Hauner 2018-12-27 10:10:45 +11:00 committed by GitHub
commit 6f4ffd8d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 412 additions and 234 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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(&registration, 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);
}
*/
}

View File

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

View File

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

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

View 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" }

View 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);
}
}

View 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(&registration, 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
);
}
}

View 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};

View File

@ -5,4 +5,5 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
bls = { path = "../utils/bls" }
types = { path = "../types" }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub struct ShardReassignmentRecord {
pub validator_index: u64,
pub shard: u64,

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
extern crate honey_badger_split;
extern crate spec;
extern crate types;
extern crate vec_shuffle;

View File

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