mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 18:32:42 +00:00
Modify genesis processing process.
- Removed BeaconStateBuilder - Added genesis code to `state_processing`.
This commit is contained in:
@@ -10,9 +10,6 @@ use ssz_derive::{Decode, Encode, TreeHash};
|
||||
use std::collections::HashMap;
|
||||
use test_random_derive::TestRandom;
|
||||
|
||||
pub use builder::BeaconStateBuilder;
|
||||
|
||||
mod builder;
|
||||
mod epoch_cache;
|
||||
pub mod helpers;
|
||||
mod pubkey_cache;
|
||||
@@ -32,7 +29,8 @@ pub enum Error {
|
||||
InvalidBitfield,
|
||||
ValidatorIsWithdrawable,
|
||||
InsufficientRandaoMixes,
|
||||
InsufficientValidators,
|
||||
NoValidators,
|
||||
UnableToDetermineProducer,
|
||||
InsufficientBlockRoots,
|
||||
InsufficientIndexRoots,
|
||||
InsufficientAttestations,
|
||||
@@ -534,7 +532,7 @@ impl BeaconState {
|
||||
///
|
||||
/// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn get_beacon_proposer_index(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -547,14 +545,16 @@ impl BeaconState {
|
||||
.get_crosslink_committees_at_slot(slot, spec)
|
||||
.ok_or_else(|| Error::SlotOutOfBounds)?;
|
||||
|
||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
||||
|
||||
committees
|
||||
.first()
|
||||
.ok_or(Error::InsufficientValidators)
|
||||
.ok_or(Error::UnableToDetermineProducer)
|
||||
.and_then(|first| {
|
||||
let index = slot
|
||||
let index = epoch
|
||||
.as_usize()
|
||||
.checked_rem(first.committee.len())
|
||||
.ok_or(Error::InsufficientValidators)?;
|
||||
.ok_or(Error::UnableToDetermineProducer)?;
|
||||
Ok(first.committee[index])
|
||||
})
|
||||
}
|
||||
@@ -581,103 +581,9 @@ impl BeaconState {
|
||||
epoch + 1 + spec.activation_exit_delay
|
||||
}
|
||||
|
||||
/// Process multiple deposits in sequence.
|
||||
///
|
||||
/// Builds a hashmap of validator pubkeys to validator index and passes it to each successive
|
||||
/// call to `process_deposit(..)`. This requires much less computation than successive calls to
|
||||
/// `process_deposits(..)` without the hashmap.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn process_deposits(
|
||||
&mut self,
|
||||
deposits: Vec<&DepositData>,
|
||||
spec: &ChainSpec,
|
||||
) -> Vec<usize> {
|
||||
let mut added_indices = vec![];
|
||||
let mut pubkey_map: HashMap<PublicKey, usize> = HashMap::new();
|
||||
|
||||
for (i, validator) in self.validator_registry.iter().enumerate() {
|
||||
pubkey_map.insert(validator.pubkey.clone(), i);
|
||||
}
|
||||
|
||||
for deposit_data in deposits {
|
||||
let result = self.process_deposit(
|
||||
deposit_data.deposit_input.clone(),
|
||||
deposit_data.amount,
|
||||
Some(&pubkey_map),
|
||||
spec,
|
||||
);
|
||||
if let Ok(index) = result {
|
||||
added_indices.push(index);
|
||||
}
|
||||
}
|
||||
added_indices
|
||||
}
|
||||
|
||||
/// Process a validator deposit, returning the validator index if the deposit is valid.
|
||||
///
|
||||
/// Optionally accepts a hashmap of all validator pubkeys to their validator index. Without
|
||||
/// this hashmap, each call to `process_deposits` requires an iteration though
|
||||
/// `self.validator_registry`. This becomes highly inefficient at scale.
|
||||
///
|
||||
/// TODO: this function also exists in a more optimal form in the `state_processing` crate as
|
||||
/// `process_deposits`; unify these two functions.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn process_deposit(
|
||||
&mut self,
|
||||
deposit_input: DepositInput,
|
||||
amount: u64,
|
||||
pubkey_map: Option<&HashMap<PublicKey, usize>>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<usize, ()> {
|
||||
let proof_is_valid = deposit_input.proof_of_possession.verify(
|
||||
&deposit_input.signed_root(),
|
||||
spec.get_domain(self.current_epoch(&spec), Domain::Deposit, &self.fork),
|
||||
&deposit_input.pubkey,
|
||||
);
|
||||
|
||||
if !proof_is_valid {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let pubkey = deposit_input.pubkey.clone();
|
||||
let withdrawal_credentials = deposit_input.withdrawal_credentials.clone();
|
||||
|
||||
let validator_index = if let Some(pubkey_map) = pubkey_map {
|
||||
pubkey_map.get(&pubkey).and_then(|i| Some(*i))
|
||||
} else {
|
||||
self.validator_registry
|
||||
.iter()
|
||||
.position(|v| v.pubkey == pubkey)
|
||||
};
|
||||
|
||||
if let Some(index) = validator_index {
|
||||
if self.validator_registry[index].withdrawal_credentials == withdrawal_credentials {
|
||||
safe_add_assign!(self.validator_balances[index], amount);
|
||||
Ok(index)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
let validator = Validator {
|
||||
pubkey,
|
||||
withdrawal_credentials,
|
||||
activation_epoch: spec.far_future_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
initiated_exit: false,
|
||||
slashed: false,
|
||||
};
|
||||
self.validator_registry.push(validator);
|
||||
self.validator_balances.push(amount);
|
||||
Ok(self.validator_registry.len() - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Activate the validator of the given ``index``.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn activate_validator(
|
||||
&mut self,
|
||||
validator_index: usize,
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
use super::BeaconStateError;
|
||||
use crate::validator_registry::get_active_validator_indices;
|
||||
use crate::*;
|
||||
use rayon::prelude::*;
|
||||
use ssz::TreeHash;
|
||||
|
||||
/// Builds a `BeaconState` for use in production.
|
||||
///
|
||||
/// This struct should _not_ be modified for use in testing scenarios. Use `TestingBeaconStateBuilder` for that purpose.
|
||||
///
|
||||
/// This struct should remain safe and sensible for production usage.
|
||||
pub struct BeaconStateBuilder {
|
||||
pub state: BeaconState,
|
||||
}
|
||||
|
||||
impl BeaconStateBuilder {
|
||||
/// Create a new builder with the given number of validators.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn new(genesis_time: u64, latest_eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
|
||||
Self {
|
||||
state: BeaconState::genesis(genesis_time, latest_eth1_data, spec),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process deposit objects.
|
||||
///
|
||||
/// 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);
|
||||
|
||||
self.activate_genesis_validators(spec);
|
||||
|
||||
self.state.deposit_index = initial_validator_deposits.len() as u64;
|
||||
}
|
||||
|
||||
fn activate_genesis_validators(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Instantiate the validator registry from a YAML file.
|
||||
///
|
||||
/// This skips a lot of signing and verification, useful if signing and verification has been
|
||||
/// completed previously.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn import_existing_validators(
|
||||
&mut self,
|
||||
validators: Vec<Validator>,
|
||||
initial_balances: Vec<u64>,
|
||||
deposit_index: u64,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
self.state.validator_registry = validators;
|
||||
|
||||
assert_eq!(
|
||||
self.state.validator_registry.len(),
|
||||
initial_balances.len(),
|
||||
"Not enough balances for validators"
|
||||
);
|
||||
|
||||
self.state.validator_balances = initial_balances;
|
||||
|
||||
self.activate_genesis_validators(spec);
|
||||
|
||||
self.state.deposit_index = deposit_index;
|
||||
}
|
||||
|
||||
/// Updates the final state variables and returns a fully built genesis state.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn build(mut self, spec: &ChainSpec) -> Result<BeaconState, BeaconStateError> {
|
||||
let genesis_active_index_root =
|
||||
get_active_validator_indices(&self.state.validator_registry, spec.genesis_epoch)
|
||||
.hash_tree_root();
|
||||
|
||||
self.state.latest_active_index_roots = vec![
|
||||
Hash256::from_slice(&genesis_active_index_root);
|
||||
spec.latest_active_index_roots_length
|
||||
];
|
||||
|
||||
self.state.current_shuffling_seed = self.state.generate_seed(spec.genesis_epoch, spec)?;
|
||||
|
||||
Ok(self.state)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ use honey_badger_split::SplitExt;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use swap_or_not_shuffle::shuffle_list;
|
||||
|
||||
mod tests;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct EpochCache {
|
||||
/// `Some(epoch)` if the cache is initialized, where `epoch` is the cache it holds.
|
||||
@@ -247,7 +249,7 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
|
||||
pub fn build(self, spec: &ChainSpec) -> Result<EpochCrosslinkCommittees, BeaconStateError> {
|
||||
if self.active_validator_indices.is_empty() {
|
||||
return Err(Error::InsufficientValidators);
|
||||
return Err(Error::NoValidators);
|
||||
}
|
||||
|
||||
let shuffled_active_validator_indices = shuffle_list(
|
||||
@@ -277,7 +279,7 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
let crosslink_committee = CrosslinkCommittee {
|
||||
slot,
|
||||
shard,
|
||||
committee: committees.remove(j),
|
||||
committee: committees[j].drain(..).collect(),
|
||||
};
|
||||
epoch_crosslink_committees.crosslink_committees[i].push(crosslink_committee);
|
||||
|
||||
|
||||
142
eth2/types/src/beacon_state/epoch_cache/tests.rs
Normal file
142
eth2/types/src/beacon_state/epoch_cache/tests.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use crate::test_utils::*;
|
||||
use swap_or_not_shuffle::shuffle_list;
|
||||
|
||||
fn do_sane_cache_test(
|
||||
state: BeaconState,
|
||||
epoch: Epoch,
|
||||
validator_count: usize,
|
||||
expected_seed: Hash256,
|
||||
expected_shuffling_start: u64,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let active_indices: Vec<usize> = (0..validator_count).collect();
|
||||
assert_eq!(
|
||||
&active_indices[..],
|
||||
state.get_active_validator_indices(epoch, &spec).unwrap(),
|
||||
"Validator indices mismatch"
|
||||
);
|
||||
|
||||
let shuffling = shuffle_list(
|
||||
active_indices,
|
||||
spec.shuffle_round_count,
|
||||
&expected_seed[..],
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let committees_per_epoch = spec.get_epoch_committee_count(shuffling.len());
|
||||
let committees_per_slot = committees_per_epoch / spec.slots_per_epoch;
|
||||
|
||||
let mut expected_indices_iter = shuffling.iter();
|
||||
let mut shard_counter = expected_shuffling_start;
|
||||
|
||||
for (i, slot) in epoch.slot_iter(spec.slots_per_epoch).enumerate() {
|
||||
let crosslink_committees_at_slot =
|
||||
state.get_crosslink_committees_at_slot(slot, &spec).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
crosslink_committees_at_slot.len(),
|
||||
committees_per_slot as usize,
|
||||
"Bad committees per slot ({})",
|
||||
i
|
||||
);
|
||||
|
||||
for c in crosslink_committees_at_slot {
|
||||
assert_eq!(c.shard, shard_counter, "Bad shard");
|
||||
shard_counter += 1;
|
||||
shard_counter %= spec.shard_count;
|
||||
|
||||
for &i in &c.committee {
|
||||
assert_eq!(
|
||||
i,
|
||||
*expected_indices_iter.next().unwrap(),
|
||||
"Non-sequential validators."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_sane_cache_test(validator_count: usize, spec: &ChainSpec) -> BeaconState {
|
||||
let mut builder =
|
||||
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, spec);
|
||||
|
||||
let epoch = spec.genesis_epoch + 4;
|
||||
let slot = epoch.start_slot(spec.slots_per_epoch);
|
||||
builder.teleport_to_slot(slot, spec);
|
||||
|
||||
let (mut state, _keypairs) = builder.build();
|
||||
|
||||
state.current_shuffling_start_shard = 0;
|
||||
state.current_shuffling_seed = Hash256::from_slice(&[1; 32]);
|
||||
|
||||
state.previous_shuffling_start_shard = spec.shard_count - 1;
|
||||
state.previous_shuffling_seed = Hash256::from_slice(&[2; 32]);
|
||||
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::Previous, spec)
|
||||
.unwrap();
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::Current, spec)
|
||||
.unwrap();
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::NextWithRegistryChange, spec)
|
||||
.unwrap();
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::NextWithoutRegistryChange, spec)
|
||||
.unwrap();
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_sane_current_epoch_cache() {
|
||||
let mut spec = ChainSpec::few_validators();
|
||||
spec.shard_count = 4;
|
||||
let validator_count = (spec.shard_count * spec.target_committee_size) + 1;
|
||||
let state = setup_sane_cache_test(validator_count as usize, &spec);
|
||||
do_sane_cache_test(
|
||||
state.clone(),
|
||||
state.current_epoch(&spec),
|
||||
validator_count as usize,
|
||||
state.current_shuffling_seed,
|
||||
state.current_shuffling_start_shard,
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_sane_previous_epoch_cache() {
|
||||
let mut spec = ChainSpec::few_validators();
|
||||
spec.shard_count = 2;
|
||||
let validator_count = (spec.shard_count * spec.target_committee_size) + 1;
|
||||
let state = setup_sane_cache_test(validator_count as usize, &spec);
|
||||
do_sane_cache_test(
|
||||
state.clone(),
|
||||
state.previous_epoch(&spec),
|
||||
validator_count as usize,
|
||||
state.previous_shuffling_seed,
|
||||
state.previous_shuffling_start_shard,
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_sane_next_without_update_epoch_cache() {
|
||||
let mut spec = ChainSpec::few_validators();
|
||||
spec.shard_count = 2;
|
||||
let validator_count = (spec.shard_count * spec.target_committee_size) + 1;
|
||||
let mut state = setup_sane_cache_test(validator_count as usize, &spec);
|
||||
state.validator_registry_update_epoch = state.slot.epoch(spec.slots_per_epoch);
|
||||
do_sane_cache_test(
|
||||
state.clone(),
|
||||
state.next_epoch(&spec),
|
||||
validator_count as usize,
|
||||
state.current_shuffling_seed,
|
||||
state.current_shuffling_start_shard,
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::{generate_deterministic_keypairs, KeypairsFile};
|
||||
use crate::beacon_state::BeaconStateBuilder;
|
||||
use crate::test_utils::TestingPendingAttestationBuilder;
|
||||
use crate::*;
|
||||
use bls::get_withdrawal_credentials;
|
||||
@@ -110,7 +109,8 @@ impl TestingBeaconStateBuilder {
|
||||
Validator {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_credentials,
|
||||
activation_epoch: spec.far_future_epoch,
|
||||
// All validators start active.
|
||||
activation_epoch: spec.genesis_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
initiated_exit: false,
|
||||
@@ -119,7 +119,7 @@ impl TestingBeaconStateBuilder {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut state_builder = BeaconStateBuilder::new(
|
||||
let mut state = BeaconState::genesis(
|
||||
0,
|
||||
Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
@@ -131,14 +131,8 @@ impl TestingBeaconStateBuilder {
|
||||
let balances = vec![32_000_000_000; validator_count];
|
||||
|
||||
debug!("Importing {} existing validators...", validator_count);
|
||||
state_builder.import_existing_validators(
|
||||
validators,
|
||||
balances,
|
||||
validator_count as u64,
|
||||
spec,
|
||||
);
|
||||
|
||||
let state = state_builder.build(spec).unwrap();
|
||||
state.validator_registry = validators;
|
||||
state.validator_balances = balances;
|
||||
|
||||
debug!("BeaconState built.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user