mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 21:34:46 +00:00
Merge latest interop
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use crate::checkpoint::CheckPoint;
|
||||
use crate::errors::{BeaconChainError as Error, BlockProductionError};
|
||||
use crate::eth1_chain::{Eth1Chain, Eth1ChainBackend};
|
||||
use crate::fork_choice::{Error as ForkChoiceError, ForkChoice};
|
||||
use crate::iter::{ReverseBlockRootIterator, ReverseStateRootIterator};
|
||||
use crate::metrics;
|
||||
@@ -45,11 +46,14 @@ pub enum BlockProcessingOutcome {
|
||||
block_slot: Slot,
|
||||
},
|
||||
/// The block state_root does not match the generated state.
|
||||
StateRootMismatch,
|
||||
StateRootMismatch { block: Hash256, local: Hash256 },
|
||||
/// The block was a genesis block, these blocks cannot be re-imported.
|
||||
GenesisBlock,
|
||||
/// The slot is finalized, no need to import.
|
||||
FinalizedSlot,
|
||||
WouldRevertFinalizedSlot {
|
||||
block_slot: Slot,
|
||||
finalized_slot: Slot,
|
||||
},
|
||||
/// Block is already known, no need to re-import.
|
||||
BlockIsAlreadyKnown,
|
||||
/// The block could not be applied to the state, it is invalid.
|
||||
@@ -76,25 +80,31 @@ pub enum AttestationProcessingOutcome {
|
||||
Invalid(AttestationValidationError),
|
||||
}
|
||||
|
||||
pub enum StateCow<'a, T: EthSpec> {
|
||||
/// Effectively a `Cow<BeaconState>`, however when it is `Borrowed` it holds a `RwLockReadGuard` (a
|
||||
/// read-lock on some read/write-locked state).
|
||||
///
|
||||
/// Only has a small subset of the functionality of a `std::borrow::Cow`.
|
||||
pub enum BeaconStateCow<'a, T: EthSpec> {
|
||||
Borrowed(RwLockReadGuard<'a, CheckPoint<T>>),
|
||||
Owned(BeaconState<T>),
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> AsRef<BeaconState<T>> for StateCow<'a, T> {
|
||||
fn as_ref(&self) -> &BeaconState<T> {
|
||||
impl<'a, T: EthSpec> BeaconStateCow<'a, T> {
|
||||
pub fn maybe_as_mut_ref(&mut self) -> Option<&mut BeaconState<T>> {
|
||||
match self {
|
||||
StateCow::Borrowed(checkpoint) => &checkpoint.beacon_state,
|
||||
StateCow::Owned(state) => &state,
|
||||
BeaconStateCow::Borrowed(_) => None,
|
||||
BeaconStateCow::Owned(ref mut state) => Some(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> StateCow<'a, T> {
|
||||
pub fn as_mut_ref(&mut self) -> Option<&mut BeaconState<T>> {
|
||||
impl<'a, T: EthSpec> std::ops::Deref for BeaconStateCow<'a, T> {
|
||||
type Target = BeaconState<T>;
|
||||
|
||||
fn deref(&self) -> &BeaconState<T> {
|
||||
match self {
|
||||
StateCow::Borrowed(_) => None,
|
||||
StateCow::Owned(ref mut state) => Some(state),
|
||||
BeaconStateCow::Borrowed(checkpoint) => &checkpoint.beacon_state,
|
||||
BeaconStateCow::Owned(state) => &state,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +113,7 @@ pub trait BeaconChainTypes: Send + Sync + 'static {
|
||||
type Store: store::Store;
|
||||
type SlotClock: slot_clock::SlotClock;
|
||||
type LmdGhost: LmdGhost<Self::Store, Self::EthSpec>;
|
||||
type Eth1Chain: Eth1ChainBackend<Self::EthSpec>;
|
||||
type EthSpec: types::EthSpec;
|
||||
}
|
||||
|
||||
@@ -117,6 +128,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
/// Stores all operations (e.g., `Attestation`, `Deposit`, etc) that are candidates for
|
||||
/// inclusion in a block.
|
||||
pub op_pool: OperationPool<T::EthSpec>,
|
||||
/// Provides information from the Ethereum 1 (PoW) chain.
|
||||
pub eth1_chain: Eth1Chain<T>,
|
||||
/// Stores a "snapshot" of the chain at the time the head-of-the-chain block was received.
|
||||
canonical_head: RwLock<CheckPoint<T::EthSpec>>,
|
||||
/// The root of the genesis block.
|
||||
@@ -132,6 +145,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Instantiate a new Beacon Chain, from genesis.
|
||||
pub fn from_genesis(
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
mut genesis_state: BeaconState<T::EthSpec>,
|
||||
mut genesis_block: BeaconBlock<T::EthSpec>,
|
||||
spec: ChainSpec,
|
||||
@@ -176,6 +190,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
spec,
|
||||
slot_clock,
|
||||
op_pool: OperationPool::new(),
|
||||
eth1_chain: Eth1Chain::new(eth1_backend),
|
||||
canonical_head,
|
||||
genesis_block_root,
|
||||
fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root),
|
||||
@@ -187,6 +202,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Attempt to load an existing instance from the given `store`.
|
||||
pub fn from_store(
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Option<BeaconChain<T>>, Error> {
|
||||
@@ -223,6 +239,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
slot_clock,
|
||||
fork_choice: ForkChoice::new(store.clone(), last_finalized_block, last_finalized_root),
|
||||
op_pool,
|
||||
eth1_chain: Eth1Chain::new(eth1_backend),
|
||||
canonical_head: RwLock::new(p.canonical_head),
|
||||
genesis_block_root: p.genesis_block_root,
|
||||
store,
|
||||
@@ -373,11 +390,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
///
|
||||
/// Returns `None` when the state is not found in the database or there is an error skipping
|
||||
/// to a future state.
|
||||
pub fn state_at_slot(&self, slot: Slot) -> Result<StateCow<T::EthSpec>, Error> {
|
||||
pub fn state_at_slot(&self, slot: Slot) -> Result<BeaconStateCow<T::EthSpec>, Error> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
if slot == head_state.slot {
|
||||
Ok(StateCow::Borrowed(self.head()))
|
||||
Ok(BeaconStateCow::Borrowed(self.head()))
|
||||
} else if slot > head_state.slot {
|
||||
let head_state_slot = head_state.slot;
|
||||
let mut state = head_state.clone();
|
||||
@@ -397,7 +414,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(StateCow::Owned(state))
|
||||
Ok(BeaconStateCow::Owned(state))
|
||||
} else {
|
||||
let state_root = self
|
||||
.rev_iter_state_roots()
|
||||
@@ -405,7 +422,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.map(|(root, _slot)| root)
|
||||
.ok_or_else(|| Error::NoStateForSlot(slot))?;
|
||||
|
||||
Ok(StateCow::Owned(
|
||||
Ok(BeaconStateCow::Owned(
|
||||
self.store
|
||||
.get(&state_root)?
|
||||
.ok_or_else(|| Error::NoStateForSlot(slot))?,
|
||||
@@ -421,7 +438,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
///
|
||||
/// Returns `None` when there is an error skipping to a future state or the slot clock cannot
|
||||
/// be read.
|
||||
pub fn state_now(&self) -> Result<StateCow<T::EthSpec>, Error> {
|
||||
pub fn state_now(&self) -> Result<BeaconStateCow<T::EthSpec>, Error> {
|
||||
self.state_at_slot(self.slot()?)
|
||||
}
|
||||
|
||||
@@ -473,25 +490,24 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
let mut state = if epoch(slot) == epoch(head_state.slot) {
|
||||
StateCow::Borrowed(self.head())
|
||||
BeaconStateCow::Borrowed(self.head())
|
||||
} else {
|
||||
self.state_at_slot(slot)?
|
||||
};
|
||||
|
||||
if let Some(state) = state.as_mut_ref() {
|
||||
if let Some(state) = state.maybe_as_mut_ref() {
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
}
|
||||
|
||||
if epoch(state.as_ref().slot) != epoch(slot) {
|
||||
if epoch(state.slot) != epoch(slot) {
|
||||
return Err(Error::InvariantViolated(format!(
|
||||
"Epochs in consistent in proposer lookup: state: {}, requested: {}",
|
||||
epoch(state.as_ref().slot),
|
||||
epoch(state.slot),
|
||||
epoch(slot)
|
||||
)));
|
||||
}
|
||||
|
||||
state
|
||||
.as_ref()
|
||||
.get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -509,26 +525,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_state = &self.head().beacon_state;
|
||||
|
||||
let mut state = if epoch == as_epoch(head_state.slot) {
|
||||
StateCow::Borrowed(self.head())
|
||||
BeaconStateCow::Borrowed(self.head())
|
||||
} else {
|
||||
self.state_at_slot(epoch.start_slot(T::EthSpec::slots_per_epoch()))?
|
||||
};
|
||||
|
||||
if let Some(state) = state.as_mut_ref() {
|
||||
if let Some(state) = state.maybe_as_mut_ref() {
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
}
|
||||
|
||||
if as_epoch(state.as_ref().slot) != epoch {
|
||||
if as_epoch(state.slot) != epoch {
|
||||
return Err(Error::InvariantViolated(format!(
|
||||
"Epochs in consistent in attestation duties lookup: state: {}, requested: {}",
|
||||
as_epoch(state.as_ref().slot),
|
||||
as_epoch(state.slot),
|
||||
epoch
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(attestation_duty) = state
|
||||
.as_ref()
|
||||
.get_attestation_duties(validator_index, RelativeEpoch::Current)?
|
||||
if let Some(attestation_duty) =
|
||||
state.get_attestation_duties(validator_index, RelativeEpoch::Current)?
|
||||
{
|
||||
Ok(Some((attestation_duty.slot, attestation_duty.shard)))
|
||||
} else {
|
||||
@@ -549,12 +564,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let head_block_root = self.head().beacon_block_root;
|
||||
let head_block_slot = self.head().beacon_block.slot;
|
||||
|
||||
self.produce_attestation_data_for_block(
|
||||
shard,
|
||||
head_block_root,
|
||||
head_block_slot,
|
||||
state.as_ref(),
|
||||
)
|
||||
self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, &*state)
|
||||
}
|
||||
|
||||
/// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`.
|
||||
@@ -876,7 +886,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => self
|
||||
.op_pool
|
||||
.insert_voluntary_exit(exit, state.as_ref(), &self.spec),
|
||||
.insert_voluntary_exit(exit, &*state, &self.spec),
|
||||
Err(e) => {
|
||||
error!(
|
||||
&self.log,
|
||||
@@ -892,9 +902,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Accept some transfer and queue it for inclusion in an appropriate block.
|
||||
pub fn process_transfer(&self, transfer: Transfer) -> Result<(), TransferValidationError> {
|
||||
match self.state_now() {
|
||||
Ok(state) => self
|
||||
.op_pool
|
||||
.insert_transfer(transfer, state.as_ref(), &self.spec),
|
||||
Ok(state) => self.op_pool.insert_transfer(transfer, &*state, &self.spec),
|
||||
Err(e) => {
|
||||
error!(
|
||||
&self.log,
|
||||
@@ -915,7 +923,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => {
|
||||
self.op_pool
|
||||
.insert_proposer_slashing(proposer_slashing, state.as_ref(), &self.spec)
|
||||
.insert_proposer_slashing(proposer_slashing, &*state, &self.spec)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@@ -937,7 +945,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
match self.state_now() {
|
||||
Ok(state) => {
|
||||
self.op_pool
|
||||
.insert_attester_slashing(attester_slashing, state.as_ref(), &self.spec)
|
||||
.insert_attester_slashing(attester_slashing, &*state, &self.spec)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@@ -968,14 +976,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if block.slot <= finalized_slot {
|
||||
return Ok(BlockProcessingOutcome::FinalizedSlot);
|
||||
}
|
||||
|
||||
if block.slot == 0 {
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
}
|
||||
|
||||
if block.slot <= finalized_slot {
|
||||
return Ok(BlockProcessingOutcome::WouldRevertFinalizedSlot {
|
||||
block_slot: block.slot,
|
||||
finalized_slot: finalized_slot,
|
||||
});
|
||||
}
|
||||
|
||||
let block_root_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_BLOCK_ROOT);
|
||||
|
||||
let block_root = block.canonical_root();
|
||||
@@ -1073,7 +1084,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
if block.state_root != state_root {
|
||||
return Ok(BlockProcessingOutcome::StateRootMismatch);
|
||||
return Ok(BlockProcessingOutcome::StateRootMismatch {
|
||||
block: block.state_root,
|
||||
local: state_root,
|
||||
});
|
||||
}
|
||||
|
||||
metrics::stop_timer(state_root_timer);
|
||||
@@ -1158,7 +1172,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.state_at_slot(slot - 1)
|
||||
.map_err(|_| BlockProductionError::UnableToProduceAtSlot(slot))?;
|
||||
|
||||
self.produce_block_on_state(state.as_ref().clone(), slot, randao_reveal)
|
||||
self.produce_block_on_state(state.clone(), slot, randao_reveal)
|
||||
}
|
||||
|
||||
/// Produce a block for some `slot` upon the given `state`.
|
||||
@@ -1207,16 +1221,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal,
|
||||
// TODO: replace with real data.
|
||||
eth1_data: Eth1Data {
|
||||
deposit_count: state.eth1_data.deposit_count,
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
},
|
||||
eth1_data: self.eth1_chain.eth1_data_for_block_production(&state)?,
|
||||
graffiti,
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings.into(),
|
||||
attestations: self.op_pool.get_attestations(&state, &self.spec).into(),
|
||||
deposits: self.op_pool.get_deposits(&state).into(),
|
||||
deposits: self.eth1_chain.deposits_for_block_inclusion(&state)?.into(),
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(),
|
||||
transfers: self.op_pool.get_transfers(&state, &self.spec).into(),
|
||||
},
|
||||
|
||||
@@ -4,16 +4,17 @@ use lighthouse_bootstrap::Bootstrapper;
|
||||
use merkle_proof::MerkleTree;
|
||||
use rayon::prelude::*;
|
||||
use slog::Logger;
|
||||
use ssz::Encode;
|
||||
use ssz::{Decode, Encode};
|
||||
use state_processing::initialize_beacon_state_from_eth1;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, BeaconBlock, BeaconState, ChainSpec, Deposit,
|
||||
DepositData, Domain, EthSpec, Fork, Hash256, PublicKey, Signature,
|
||||
BeaconBlock, BeaconState, ChainSpec, Deposit, DepositData, Domain, EthSpec, Fork, Hash256,
|
||||
Keypair, PublicKey, Signature,
|
||||
};
|
||||
|
||||
enum BuildStrategy<T: BeaconChainTypes> {
|
||||
@@ -32,21 +33,21 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
pub fn recent_genesis(
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
minutes: u64,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, String> {
|
||||
Self::quick_start(recent_genesis_time(minutes), validator_count, spec, log)
|
||||
Self::quick_start(recent_genesis_time(minutes), keypairs, spec, log)
|
||||
}
|
||||
|
||||
pub fn quick_start(
|
||||
genesis_time: u64,
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, String> {
|
||||
let genesis_state = interop_genesis_state(validator_count, genesis_time, &spec)?;
|
||||
let genesis_state = interop_genesis_state(keypairs, genesis_time, &spec)?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
@@ -61,8 +62,32 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn ssz_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let mut file = File::open(file.clone())
|
||||
.map_err(|e| format!("Unable to open SSZ genesis state file {:?}: {:?}", file, e))?;
|
||||
|
||||
let mut bytes = vec![];
|
||||
file.read_to_end(&mut bytes)
|
||||
.map_err(|e| format!("Failed to read SSZ file: {:?}", e))?;
|
||||
|
||||
let genesis_state = BeaconState::from_ssz_bytes(&bytes)
|
||||
.map_err(|e| format!("Unable to parse SSZ genesis state file: {:?}", e))?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn json_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let file = File::open(file.clone())
|
||||
.map_err(|e| format!("Unable to open JSON genesis state file {:?}: {:?}", file, e))?;
|
||||
|
||||
let genesis_state = serde_json::from_reader(file)
|
||||
.map_err(|e| format!("Unable to parse JSON genesis state file: {:?}", e))?;
|
||||
|
||||
Ok(Self::from_genesis_state(genesis_state, spec, log))
|
||||
}
|
||||
|
||||
pub fn http_bootstrap(server: &str, spec: ChainSpec, log: Logger) -> Result<Self, String> {
|
||||
let bootstrapper = Bootstrapper::from_server_string(server.to_string())
|
||||
let bootstrapper = Bootstrapper::connect(server.to_string(), &log)
|
||||
.map_err(|e| format!("Failed to initialize bootstrap client: {}", e))?;
|
||||
|
||||
let (genesis_state, genesis_block) = bootstrapper
|
||||
@@ -102,16 +127,23 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self, store: Arc<T::Store>) -> Result<BeaconChain<T>, String> {
|
||||
pub fn build(
|
||||
self,
|
||||
store: Arc<T::Store>,
|
||||
eth1_backend: T::Eth1Chain,
|
||||
) -> Result<BeaconChain<T>, String> {
|
||||
Ok(match self.build_strategy {
|
||||
BuildStrategy::LoadFromStore => BeaconChain::from_store(store, self.spec, self.log)
|
||||
.map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
|
||||
.ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?,
|
||||
BuildStrategy::LoadFromStore => {
|
||||
BeaconChain::from_store(store, eth1_backend, self.spec, self.log)
|
||||
.map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
|
||||
.ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?
|
||||
}
|
||||
BuildStrategy::FromGenesis {
|
||||
genesis_block,
|
||||
genesis_state,
|
||||
} => BeaconChain::from_genesis(
|
||||
store,
|
||||
eth1_backend,
|
||||
genesis_state.as_ref().clone(),
|
||||
genesis_block.as_ref().clone(),
|
||||
self.spec,
|
||||
@@ -135,12 +167,11 @@ fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -
|
||||
/// Reference:
|
||||
/// https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start
|
||||
fn interop_genesis_state<T: EthSpec>(
|
||||
validator_count: usize,
|
||||
keypairs: &[Keypair],
|
||||
genesis_time: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<T>, String> {
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
let eth1_block_hash = Hash256::from_slice(&[42; 32]);
|
||||
let eth1_block_hash = Hash256::from_slice(&[0x42; 32]);
|
||||
let eth1_timestamp = 2_u64.pow(40);
|
||||
let amount = spec.max_effective_balance;
|
||||
|
||||
@@ -155,7 +186,7 @@ fn interop_genesis_state<T: EthSpec>(
|
||||
.map(|keypair| {
|
||||
let mut data = DepositData {
|
||||
withdrawal_credentials: withdrawal_credentials(&keypair.pk),
|
||||
pubkey: keypair.pk.into(),
|
||||
pubkey: keypair.pk.clone().into(),
|
||||
amount,
|
||||
signature: Signature::empty_signature().into(),
|
||||
};
|
||||
@@ -212,6 +243,9 @@ fn interop_genesis_state<T: EthSpec>(
|
||||
|
||||
state.genesis_time = genesis_time;
|
||||
|
||||
// Invalid all the caches after all the manual state surgery.
|
||||
state.drop_all_caches();
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
@@ -237,7 +271,7 @@ fn recent_genesis_time(minutes: u64) -> u64 {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use types::{EthSpec, MinimalEthSpec};
|
||||
use types::{test_utils::generate_deterministic_keypairs, EthSpec, MinimalEthSpec};
|
||||
|
||||
type TestEthSpec = MinimalEthSpec;
|
||||
|
||||
@@ -247,12 +281,14 @@ mod test {
|
||||
let genesis_time = 42;
|
||||
let spec = &TestEthSpec::default_spec();
|
||||
|
||||
let state = interop_genesis_state::<TestEthSpec>(validator_count, genesis_time, spec)
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
|
||||
let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec)
|
||||
.expect("should build state");
|
||||
|
||||
assert_eq!(
|
||||
state.eth1_data.block_hash,
|
||||
Hash256::from_slice(&[42; 32]),
|
||||
Hash256::from_slice(&[0x42; 32]),
|
||||
"eth1 block hash should be co-ordinated junk"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::eth1_chain::Error as Eth1ChainError;
|
||||
use crate::fork_choice::Error as ForkChoiceError;
|
||||
use state_processing::per_block_processing::errors::AttestationValidationError;
|
||||
use state_processing::BlockProcessingError;
|
||||
@@ -33,6 +34,7 @@ pub enum BeaconChainError {
|
||||
MissingBeaconBlock(Hash256),
|
||||
MissingBeaconState(Hash256),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
UnableToAdvanceState(String),
|
||||
NoStateForAttestation {
|
||||
beacon_block_root: Hash256,
|
||||
},
|
||||
@@ -42,6 +44,7 @@ pub enum BeaconChainError {
|
||||
}
|
||||
|
||||
easy_from_to!(SlotProcessingError, BeaconChainError);
|
||||
easy_from_to!(AttestationValidationError, BeaconChainError);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockProductionError {
|
||||
@@ -50,10 +53,11 @@ pub enum BlockProductionError {
|
||||
UnableToProduceAtSlot(Slot),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
Eth1ChainError(Eth1ChainError),
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
easy_from_to!(BlockProcessingError, BlockProductionError);
|
||||
easy_from_to!(BeaconStateError, BlockProductionError);
|
||||
easy_from_to!(SlotProcessingError, BlockProductionError);
|
||||
easy_from_to!(AttestationValidationError, BeaconChainError);
|
||||
easy_from_to!(Eth1ChainError, BlockProductionError);
|
||||
|
||||
110
beacon_node/beacon_chain/src/eth1_chain.rs
Normal file
110
beacon_node/beacon_chain/src/eth1_chain.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use crate::BeaconChainTypes;
|
||||
use eth2_hashing::hash;
|
||||
use std::marker::PhantomData;
|
||||
use types::{BeaconState, Deposit, Eth1Data, EthSpec, Hash256};
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Holds an `Eth1ChainBackend` and serves requests from the `BeaconChain`.
|
||||
pub struct Eth1Chain<T: BeaconChainTypes> {
|
||||
backend: T::Eth1Chain,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> Eth1Chain<T> {
|
||||
pub fn new(backend: T::Eth1Chain) -> Self {
|
||||
Self { backend }
|
||||
}
|
||||
|
||||
/// Returns the `Eth1Data` that should be included in a block being produced for the given
|
||||
/// `state`.
|
||||
pub fn eth1_data_for_block_production(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
) -> Result<Eth1Data> {
|
||||
self.backend.eth1_data(state)
|
||||
}
|
||||
|
||||
/// Returns a list of `Deposits` that may be included in a block.
|
||||
///
|
||||
/// Including all of the returned `Deposits` in a block should _not_ cause it to become
|
||||
/// invalid.
|
||||
pub fn deposits_for_block_inclusion(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
) -> Result<Vec<Deposit>> {
|
||||
let deposits = self.backend.queued_deposits(state)?;
|
||||
|
||||
// TODO: truncate deposits if required.
|
||||
|
||||
Ok(deposits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Unable to return an Eth1Data for the given epoch.
|
||||
EpochUnavailable,
|
||||
/// An error from the backend service (e.g., the web3 data fetcher).
|
||||
BackendError(String),
|
||||
}
|
||||
|
||||
pub trait Eth1ChainBackend<T: EthSpec>: Sized + Send + Sync {
|
||||
fn new(server: String) -> Result<Self>;
|
||||
|
||||
/// Returns the `Eth1Data` that should be included in a block being produced for the given
|
||||
/// `state`.
|
||||
fn eth1_data(&self, beacon_state: &BeaconState<T>) -> Result<Eth1Data>;
|
||||
|
||||
/// Returns all `Deposits` between `state.eth1_deposit_index` and
|
||||
/// `state.eth1_data.deposit_count`.
|
||||
///
|
||||
/// # Note:
|
||||
///
|
||||
/// It is possible that not all returned `Deposits` can be included in a block. E.g., there may
|
||||
/// be more than `MAX_DEPOSIT_COUNT` or the churn may be too high.
|
||||
fn queued_deposits(&self, beacon_state: &BeaconState<T>) -> Result<Vec<Deposit>>;
|
||||
}
|
||||
|
||||
pub struct InteropEth1ChainBackend<T: EthSpec> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Eth1ChainBackend<T> for InteropEth1ChainBackend<T> {
|
||||
fn new(_server: String) -> Result<Self> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
fn eth1_data(&self, state: &BeaconState<T>) -> Result<Eth1Data> {
|
||||
let current_epoch = state.current_epoch();
|
||||
let slots_per_voting_period = T::slots_per_eth1_voting_period() as u64;
|
||||
let current_voting_period: u64 = current_epoch.as_u64() / slots_per_voting_period;
|
||||
|
||||
let deposit_root = hash(&int_to_bytes32(current_voting_period));
|
||||
let block_hash = hash(&deposit_root);
|
||||
|
||||
Ok(Eth1Data {
|
||||
deposit_root: Hash256::from_slice(&deposit_root),
|
||||
deposit_count: state.eth1_deposit_index,
|
||||
block_hash: Hash256::from_slice(&block_hash),
|
||||
})
|
||||
}
|
||||
|
||||
fn queued_deposits(&self, _: &BeaconState<T>) -> Result<Vec<Deposit>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Default for InteropEth1ChainBackend<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `int` as little-endian bytes with a length of 32.
|
||||
fn int_to_bytes32(int: u64) -> Vec<u8> {
|
||||
let mut vec = int.to_le_bytes().to_vec();
|
||||
vec.resize(32, 0);
|
||||
vec
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod beacon_chain;
|
||||
mod beacon_chain_builder;
|
||||
mod checkpoint;
|
||||
mod errors;
|
||||
mod eth1_chain;
|
||||
mod fork_choice;
|
||||
mod iter;
|
||||
mod metrics;
|
||||
@@ -18,6 +19,7 @@ pub use self::beacon_chain::{
|
||||
pub use self::checkpoint::CheckPoint;
|
||||
pub use self::errors::{BeaconChainError, BlockProductionError};
|
||||
pub use beacon_chain_builder::BeaconChainBuilder;
|
||||
pub use eth1_chain::{Eth1ChainBackend, InteropEth1ChainBackend};
|
||||
pub use lmd_ghost;
|
||||
pub use metrics::scrape_for_metrics;
|
||||
pub use parking_lot;
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
use crate::{
|
||||
AttestationProcessingOutcome, BeaconChain, BeaconChainBuilder, BeaconChainTypes,
|
||||
BlockProcessingOutcome, InteropEth1ChainBackend,
|
||||
};
|
||||
use lmd_ghost::LmdGhost;
|
||||
use rayon::prelude::*;
|
||||
use sloggers::{null::NullLoggerBuilder, Build};
|
||||
use sloggers::{terminal::TerminalLoggerBuilder, types::Severity, Build};
|
||||
use slot_clock::TestingSlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use store::MemoryStore;
|
||||
use store::Store;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation,
|
||||
AttestationDataAndCustodyBit, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec,
|
||||
Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot,
|
||||
AggregateSignature, Attestation, AttestationDataAndCustodyBit, BeaconBlock, BeaconState,
|
||||
BitList, ChainSpec, Domain, EthSpec, Hash256, Keypair, RelativeEpoch, SecretKey, Signature,
|
||||
Slot,
|
||||
};
|
||||
|
||||
pub use types::test_utils::generate_deterministic_keypairs;
|
||||
|
||||
pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
|
||||
|
||||
pub const HARNESS_GENESIS_TIME: u64 = 1567552690; // 4th September 2019
|
||||
|
||||
/// Indicates how the `BeaconChainHarness` should produce blocks.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BlockStrategy {
|
||||
@@ -60,6 +66,7 @@ where
|
||||
type Store = MemoryStore;
|
||||
type SlotClock = TestingSlotClock;
|
||||
type LmdGhost = L;
|
||||
type Eth1Chain = InteropEth1ChainBackend<E>;
|
||||
type EthSpec = E;
|
||||
}
|
||||
|
||||
@@ -83,40 +90,21 @@ where
|
||||
E: EthSpec,
|
||||
{
|
||||
/// Instantiate a new harness with `validator_count` initial validators.
|
||||
pub fn new(validator_count: usize) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
||||
validator_count,
|
||||
&E::default_spec(),
|
||||
);
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with an initial validator for each key supplied.
|
||||
pub fn from_keypairs(keypairs: Vec<Keypair>) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_keypairs(keypairs, &E::default_spec());
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with the given genesis state and a keypair for each of the
|
||||
/// initial validators in the given state.
|
||||
pub fn from_state_and_keypairs(genesis_state: BeaconState<E>, keypairs: Vec<Keypair>) -> Self {
|
||||
pub fn new(keypairs: Vec<Keypair>) -> Self {
|
||||
let spec = E::default_spec();
|
||||
|
||||
let log = TerminalLoggerBuilder::new()
|
||||
.level(Severity::Warning)
|
||||
.build()
|
||||
.expect("logger should build");
|
||||
|
||||
let store = Arc::new(MemoryStore::open());
|
||||
|
||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
|
||||
|
||||
let builder = NullLoggerBuilder;
|
||||
let log = builder.build().expect("logger should build");
|
||||
|
||||
let chain =
|
||||
BeaconChain::from_genesis(store, genesis_state, genesis_block, spec.clone(), log)
|
||||
.expect("Terminate if beacon chain generation fails");
|
||||
BeaconChainBuilder::quick_start(HARNESS_GENESIS_TIME, &keypairs, spec.clone(), log)
|
||||
.unwrap_or_else(|e| panic!("Failed to create beacon chain builder: {}", e))
|
||||
.build(store.clone(), InteropEth1ChainBackend::default())
|
||||
.unwrap_or_else(|e| panic!("Failed to build beacon chain: {}", e));
|
||||
|
||||
Self {
|
||||
chain,
|
||||
@@ -156,7 +144,10 @@ where
|
||||
BlockStrategy::ForkCanonicalChainAt { previous_slot, .. } => previous_slot,
|
||||
};
|
||||
|
||||
self.get_state_at_slot(state_slot)
|
||||
self.chain
|
||||
.state_at_slot(state_slot)
|
||||
.expect("should find state for slot")
|
||||
.clone()
|
||||
};
|
||||
|
||||
// Determine the first slot where a block should be built.
|
||||
@@ -194,21 +185,6 @@ where
|
||||
head_block_root.expect("did not produce any blocks")
|
||||
}
|
||||
|
||||
fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
|
||||
let state_root = self
|
||||
.chain
|
||||
.rev_iter_state_roots()
|
||||
.find(|(_hash, slot)| *slot == state_slot)
|
||||
.map(|(hash, _slot)| hash)
|
||||
.expect("could not find state root");
|
||||
|
||||
self.chain
|
||||
.store
|
||||
.get(&state_root)
|
||||
.expect("should read db")
|
||||
.expect("should find state root")
|
||||
}
|
||||
|
||||
/// Returns a newly created block, signed by the proposer for the given slot.
|
||||
fn build_block(
|
||||
&self,
|
||||
@@ -282,9 +258,14 @@ where
|
||||
)
|
||||
.into_iter()
|
||||
.for_each(|attestation| {
|
||||
self.chain
|
||||
match self
|
||||
.chain
|
||||
.process_attestation(attestation)
|
||||
.expect("should process attestation");
|
||||
.expect("should not error during attestation processing")
|
||||
{
|
||||
AttestationProcessingOutcome::Processed => (),
|
||||
other => panic!("did not successfully process attestation: {:?}", other),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user