2019-02-14 01:09:18 +00:00
|
|
|
use crate::checkpoint::CheckPoint;
|
2019-03-07 01:53:15 +00:00
|
|
|
use crate::errors::{BeaconChainError as Error, BlockProductionError};
|
2019-05-28 07:30:09 +00:00
|
|
|
use crate::metrics::Metrics;
|
2019-05-27 06:13:32 +00:00
|
|
|
use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
|
2019-02-14 01:09:18 +00:00
|
|
|
use fork_choice::{ForkChoice, ForkChoiceError};
|
2019-03-29 08:09:01 +00:00
|
|
|
use log::{debug, trace};
|
2019-03-29 07:54:01 +00:00
|
|
|
use operation_pool::DepositInsertStatus;
|
2019-03-29 07:30:03 +00:00
|
|
|
use operation_pool::OperationPool;
|
2019-02-14 01:09:18 +00:00
|
|
|
use parking_lot::{RwLock, RwLockReadGuard};
|
|
|
|
use slot_clock::SlotClock;
|
2019-03-30 08:11:52 +00:00
|
|
|
use state_processing::per_block_processing::errors::{
|
2019-03-29 07:54:01 +00:00
|
|
|
AttestationValidationError, AttesterSlashingValidationError, DepositValidationError,
|
|
|
|
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
|
|
|
};
|
2019-02-14 01:09:18 +00:00
|
|
|
use state_processing::{
|
2019-03-07 01:25:29 +00:00
|
|
|
per_block_processing, per_block_processing_without_verifying_block_signature,
|
|
|
|
per_slot_processing, BlockProcessingError, SlotProcessingError,
|
2019-02-14 01:09:18 +00:00
|
|
|
};
|
|
|
|
use std::sync::Arc;
|
2019-05-21 08:20:23 +00:00
|
|
|
use store::{Error as DBError, Store};
|
2019-06-03 06:13:51 +00:00
|
|
|
use tree_hash::TreeHash;
|
2019-03-17 07:10:20 +00:00
|
|
|
use types::*;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum ValidBlock {
|
|
|
|
/// The block was successfully processed.
|
|
|
|
Processed,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum InvalidBlock {
|
|
|
|
/// The block slot is greater than the present slot.
|
2019-03-24 05:35:07 +00:00
|
|
|
FutureSlot {
|
|
|
|
present_slot: Slot,
|
|
|
|
block_slot: Slot,
|
|
|
|
},
|
2019-02-14 01:09:18 +00:00
|
|
|
/// The block state_root does not match the generated state.
|
|
|
|
StateRootMismatch,
|
|
|
|
/// The blocks parent_root is unknown.
|
|
|
|
ParentUnknown,
|
|
|
|
/// There was an error whilst advancing the parent state to the present slot. This condition
|
|
|
|
/// should not occur, it likely represents an internal error.
|
|
|
|
SlotProcessingError(SlotProcessingError),
|
|
|
|
/// The block could not be applied to the state, it is invalid.
|
|
|
|
PerBlockProcessingError(BlockProcessingError),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum BlockProcessingOutcome {
|
|
|
|
/// The block was successfully validated.
|
|
|
|
ValidBlock(ValidBlock),
|
|
|
|
/// The block was not successfully validated.
|
|
|
|
InvalidBlock(InvalidBlock),
|
|
|
|
}
|
|
|
|
|
2019-03-23 07:48:09 +00:00
|
|
|
impl BlockProcessingOutcome {
|
|
|
|
/// Returns `true` if the block was objectively invalid and we should disregard the peer who
|
|
|
|
/// sent it.
|
|
|
|
pub fn is_invalid(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
BlockProcessingOutcome::ValidBlock(_) => false,
|
|
|
|
BlockProcessingOutcome::InvalidBlock(r) => match r {
|
2019-03-24 05:35:07 +00:00
|
|
|
InvalidBlock::FutureSlot { .. } => true,
|
2019-03-23 07:48:09 +00:00
|
|
|
InvalidBlock::StateRootMismatch => true,
|
|
|
|
InvalidBlock::ParentUnknown => false,
|
|
|
|
InvalidBlock::SlotProcessingError(_) => false,
|
|
|
|
InvalidBlock::PerBlockProcessingError(e) => match e {
|
|
|
|
BlockProcessingError::Invalid(_) => true,
|
|
|
|
BlockProcessingError::BeaconStateError(_) => false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2019-03-24 03:22:12 +00:00
|
|
|
|
|
|
|
/// Returns `true` if the block was successfully processed and can be removed from any import
|
|
|
|
/// queues or temporary storage.
|
|
|
|
pub fn sucessfully_processed(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
BlockProcessingOutcome::ValidBlock(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 07:48:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 10:51:15 +00:00
|
|
|
pub trait BeaconChainTypes {
|
|
|
|
type Store: store::Store;
|
|
|
|
type SlotClock: slot_clock::SlotClock;
|
2019-05-27 05:12:51 +00:00
|
|
|
type ForkChoice: fork_choice::ForkChoice<Self::Store>;
|
2019-05-25 10:51:15 +00:00
|
|
|
type EthSpec: types::EthSpec;
|
|
|
|
}
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Represents the "Beacon Chain" component of Ethereum 2.0. Allows import of blocks and block
|
|
|
|
/// operations and chooses a canonical head.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub struct BeaconChain<T: BeaconChainTypes> {
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub store: Arc<T::Store>,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Reports the current slot, typically based upon the system clock.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub slot_clock: T::SlotClock,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Stores all operations (e.g., `Attestation`, `Deposit`, etc) that are candidates for
|
|
|
|
/// inclusion in a block.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub op_pool: OperationPool<T::EthSpec>,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Stores a "snapshot" of the chain at the time the head-of-the-chain block was recieved.
|
2019-05-25 10:51:15 +00:00
|
|
|
canonical_head: RwLock<CheckPoint<T::EthSpec>>,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// The same state from `self.canonical_head`, but updated at the start of each slot with a
|
|
|
|
/// skip slot if no block is recieved. This is effectively a cache that avoids repeating calls
|
|
|
|
/// to `per_slot_processing`.
|
2019-05-30 08:38:41 +00:00
|
|
|
state: RwLock<BeaconState<T::EthSpec>>,
|
2019-06-01 05:02:19 +00:00
|
|
|
/// The root of the genesis block.
|
|
|
|
genesis_block_root: Hash256,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// A state-machine that is updated with information from the network and chooses a canonical
|
|
|
|
/// head block.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub fork_choice: RwLock<T::ForkChoice>,
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Stores metrics about this `BeaconChain`.
|
2019-05-28 07:30:09 +00:00
|
|
|
pub metrics: Metrics,
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 10:51:15 +00:00
|
|
|
impl<T: BeaconChainTypes> BeaconChain<T> {
|
2019-02-14 01:09:18 +00:00
|
|
|
/// Instantiate a new Beacon Chain, from genesis.
|
2019-03-08 04:33:45 +00:00
|
|
|
pub fn from_genesis(
|
2019-05-25 10:51:15 +00:00
|
|
|
store: Arc<T::Store>,
|
|
|
|
slot_clock: T::SlotClock,
|
|
|
|
mut genesis_state: BeaconState<T::EthSpec>,
|
2019-03-08 04:33:45 +00:00
|
|
|
genesis_block: BeaconBlock,
|
2019-02-14 01:09:18 +00:00
|
|
|
spec: ChainSpec,
|
2019-05-25 10:51:15 +00:00
|
|
|
fork_choice: T::ForkChoice,
|
2019-02-14 01:09:18 +00:00
|
|
|
) -> Result<Self, Error> {
|
|
|
|
let state_root = genesis_state.canonical_root();
|
2019-05-21 03:36:14 +00:00
|
|
|
store.put(&state_root, &genesis_state)?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-06-01 05:02:19 +00:00
|
|
|
let genesis_block_root = genesis_block.block_header().canonical_root();
|
|
|
|
store.put(&genesis_block_root, &genesis_block)?;
|
2019-06-01 02:56:35 +00:00
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
let canonical_head = RwLock::new(CheckPoint::new(
|
|
|
|
genesis_block.clone(),
|
2019-06-01 05:02:19 +00:00
|
|
|
genesis_block_root,
|
2019-02-14 01:09:18 +00:00
|
|
|
genesis_state.clone(),
|
|
|
|
state_root,
|
|
|
|
));
|
|
|
|
|
2019-03-31 02:40:12 +00:00
|
|
|
genesis_state.build_all_caches(&spec)?;
|
2019-02-22 05:14:16 +00:00
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
Ok(Self {
|
2019-05-21 03:36:14 +00:00
|
|
|
store,
|
2019-02-14 01:09:18 +00:00
|
|
|
slot_clock,
|
2019-03-30 01:26:25 +00:00
|
|
|
op_pool: OperationPool::new(),
|
2019-02-22 05:14:16 +00:00
|
|
|
state: RwLock::new(genesis_state),
|
2019-02-14 01:09:18 +00:00
|
|
|
canonical_head,
|
2019-06-01 05:02:19 +00:00
|
|
|
genesis_block_root,
|
2019-02-14 01:09:18 +00:00
|
|
|
fork_choice: RwLock::new(fork_choice),
|
2019-05-28 07:30:09 +00:00
|
|
|
metrics: Metrics::new()?,
|
2019-02-14 01:09:18 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-05-27 06:13:32 +00:00
|
|
|
/// Attempt to load an existing instance from the given `store`.
|
|
|
|
pub fn from_store(store: Arc<T::Store>) -> Result<Option<BeaconChain<T>>, Error> {
|
|
|
|
let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes());
|
|
|
|
let p: PersistedBeaconChain<T> = match store.get(&key) {
|
|
|
|
Err(e) => return Err(e.into()),
|
|
|
|
Ok(None) => return Ok(None),
|
|
|
|
Ok(Some(p)) => p,
|
|
|
|
};
|
|
|
|
|
|
|
|
let spec = T::EthSpec::spec();
|
|
|
|
|
|
|
|
let slot_clock = T::SlotClock::new(
|
|
|
|
spec.genesis_slot,
|
|
|
|
p.state.genesis_time,
|
|
|
|
spec.seconds_per_slot,
|
|
|
|
);
|
|
|
|
|
|
|
|
let fork_choice = T::ForkChoice::new(store.clone());
|
|
|
|
|
|
|
|
Ok(Some(BeaconChain {
|
|
|
|
store,
|
|
|
|
slot_clock,
|
|
|
|
op_pool: OperationPool::default(),
|
|
|
|
canonical_head: RwLock::new(p.canonical_head),
|
|
|
|
state: RwLock::new(p.state),
|
|
|
|
fork_choice: RwLock::new(fork_choice),
|
2019-06-01 05:02:19 +00:00
|
|
|
genesis_block_root: p.genesis_block_root,
|
2019-05-28 07:30:09 +00:00
|
|
|
metrics: Metrics::new()?,
|
2019-05-27 06:13:32 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to save this instance to `self.store`.
|
|
|
|
pub fn persist(&self) -> Result<(), Error> {
|
|
|
|
let p: PersistedBeaconChain<T> = PersistedBeaconChain {
|
|
|
|
canonical_head: self.canonical_head.read().clone(),
|
2019-06-01 05:02:19 +00:00
|
|
|
genesis_block_root: self.genesis_block_root,
|
2019-05-27 06:13:32 +00:00
|
|
|
state: self.state.read().clone(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes());
|
|
|
|
self.store.put(&key, &p)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-03-24 01:49:59 +00:00
|
|
|
/// Returns the beacon block body for each beacon block root in `roots`.
|
|
|
|
///
|
|
|
|
/// Fails if any root in `roots` does not have a corresponding block.
|
|
|
|
pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockBody>, Error> {
|
|
|
|
let bodies: Result<Vec<BeaconBlockBody>, _> = roots
|
|
|
|
.iter()
|
|
|
|
.map(|root| match self.get_block(root)? {
|
|
|
|
Some(block) => Ok(block.body),
|
|
|
|
None => Err(Error::DBInconsistent("Missing block".into())),
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(bodies?)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the beacon block header for each beacon block root in `roots`.
|
|
|
|
///
|
|
|
|
/// Fails if any root in `roots` does not have a corresponding block.
|
|
|
|
pub fn get_block_headers(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockHeader>, Error> {
|
|
|
|
let headers: Result<Vec<BeaconBlockHeader>, _> = roots
|
|
|
|
.iter()
|
|
|
|
.map(|root| match self.get_block(root)? {
|
|
|
|
Some(block) => Ok(block.block_header()),
|
|
|
|
None => Err(Error::DBInconsistent("Missing block".into())),
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(headers?)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `count `beacon block roots, starting from `start_slot` with an
|
|
|
|
/// interval of `skip` slots between each root.
|
2019-03-22 03:20:49 +00:00
|
|
|
///
|
|
|
|
/// ## Errors:
|
|
|
|
///
|
|
|
|
/// - `SlotOutOfBounds`: Unable to return the full specified range.
|
|
|
|
/// - `SlotOutOfBounds`: Unable to load a state from the DB.
|
|
|
|
/// - `SlotOutOfBounds`: Start slot is higher than the first slot.
|
|
|
|
/// - Other: BeaconState` is inconsistent.
|
|
|
|
pub fn get_block_roots(
|
|
|
|
&self,
|
2019-03-24 01:49:59 +00:00
|
|
|
earliest_slot: Slot,
|
|
|
|
count: usize,
|
|
|
|
skip: usize,
|
|
|
|
) -> Result<Vec<Hash256>, Error> {
|
|
|
|
let step_by = Slot::from(skip + 1);
|
2019-03-22 03:20:49 +00:00
|
|
|
|
|
|
|
let mut roots: Vec<Hash256> = vec![];
|
2019-03-24 01:49:59 +00:00
|
|
|
|
|
|
|
// The state for reading block roots. Will be updated with an older state if slots go too
|
|
|
|
// far back in history.
|
2019-03-22 03:20:49 +00:00
|
|
|
let mut state = self.state.read().clone();
|
|
|
|
|
2019-03-24 01:49:59 +00:00
|
|
|
// The final slot in this series, will be reduced by `skip` each loop iteration.
|
|
|
|
let mut slot = earliest_slot + Slot::from(count * (skip + 1)) - 1;
|
2019-03-23 02:23:44 +00:00
|
|
|
|
2019-03-24 01:49:59 +00:00
|
|
|
// If the highest slot requested is that of the current state insert the root of the
|
|
|
|
// head block, unless the head block's slot is not matching.
|
|
|
|
if slot == state.slot && self.head().beacon_block.slot == slot {
|
|
|
|
roots.push(self.head().beacon_block_root);
|
2019-03-22 03:20:49 +00:00
|
|
|
|
2019-03-24 01:49:59 +00:00
|
|
|
slot -= step_by;
|
|
|
|
} else if slot >= state.slot {
|
|
|
|
return Err(BeaconStateError::SlotOutOfBounds.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
2019-03-22 03:20:49 +00:00
|
|
|
// If the slot is within the range of the current state's block roots, append the root
|
|
|
|
// to the output vec.
|
|
|
|
//
|
2019-03-24 01:49:59 +00:00
|
|
|
// If we get `SlotOutOfBounds` error, load the oldest available historic
|
|
|
|
// state from the DB.
|
2019-05-08 08:18:17 +00:00
|
|
|
match state.get_block_root(slot) {
|
2019-03-22 03:20:49 +00:00
|
|
|
Ok(root) => {
|
2019-03-24 01:49:59 +00:00
|
|
|
if slot < earliest_slot {
|
2019-03-22 03:20:49 +00:00
|
|
|
break;
|
|
|
|
} else {
|
2019-03-24 01:49:59 +00:00
|
|
|
roots.push(*root);
|
|
|
|
slot -= step_by;
|
2019-03-22 03:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(BeaconStateError::SlotOutOfBounds) => {
|
|
|
|
// Read the earliest historic state in the current slot.
|
|
|
|
let earliest_historic_slot =
|
2019-05-25 10:51:15 +00:00
|
|
|
state.slot - Slot::from(T::EthSpec::slots_per_historical_root());
|
2019-03-22 03:20:49 +00:00
|
|
|
// Load the earlier state from disk.
|
2019-05-08 08:18:17 +00:00
|
|
|
let new_state_root = state.get_state_root(earliest_historic_slot)?;
|
2019-03-22 03:20:49 +00:00
|
|
|
|
|
|
|
// Break if the DB is unable to load the state.
|
2019-05-21 07:27:06 +00:00
|
|
|
state = match self.store.get(&new_state_root) {
|
2019-03-22 03:20:49 +00:00
|
|
|
Ok(Some(state)) => state,
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
2019-03-24 01:49:59 +00:00
|
|
|
Err(e) => return Err(e.into()),
|
2019-03-22 03:20:49 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-03-24 01:49:59 +00:00
|
|
|
// Return the results if they pass a sanity check.
|
|
|
|
if (slot <= earliest_slot) && (roots.len() == count) {
|
2019-03-23 02:23:44 +00:00
|
|
|
// Reverse the ordering of the roots. We extracted them in reverse order to make it
|
|
|
|
// simpler to lookup historic states.
|
|
|
|
//
|
|
|
|
// This is a potential optimisation target.
|
|
|
|
Ok(roots.iter().rev().cloned().collect())
|
2019-03-22 03:20:49 +00:00
|
|
|
} else {
|
2019-03-24 01:49:59 +00:00
|
|
|
Err(BeaconStateError::SlotOutOfBounds.into())
|
2019-03-22 03:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-23 02:23:44 +00:00
|
|
|
/// Returns the block at the given root, if any.
|
|
|
|
///
|
|
|
|
/// ## Errors
|
|
|
|
///
|
|
|
|
/// May return a database error.
|
|
|
|
pub fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock>, Error> {
|
2019-05-21 07:27:06 +00:00
|
|
|
Ok(self.store.get(block_root)?)
|
2019-03-23 02:23:44 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Update the canonical head to `new_head`.
|
|
|
|
fn update_canonical_head(&self, new_head: CheckPoint<T::EthSpec>) -> Result<(), Error> {
|
|
|
|
// Update the checkpoint that stores the head of the chain at the time it received the
|
|
|
|
// block.
|
|
|
|
*self.canonical_head.write() = new_head;
|
|
|
|
|
|
|
|
// Update the always-at-the-present-slot state we keep around for performance gains.
|
|
|
|
*self.state.write() = {
|
|
|
|
let mut state = self.canonical_head.read().beacon_state.clone();
|
|
|
|
|
|
|
|
let present_slot = match self.slot_clock.present_slot() {
|
|
|
|
Ok(Some(slot)) => slot,
|
|
|
|
_ => return Err(Error::UnableToReadSlot),
|
|
|
|
};
|
|
|
|
|
|
|
|
// If required, transition the new state to the present slot.
|
|
|
|
for _ in state.slot.as_u64()..present_slot.as_u64() {
|
|
|
|
per_slot_processing(&mut state, &T::EthSpec::spec())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
state.build_all_caches(&T::EthSpec::spec())?;
|
|
|
|
|
|
|
|
state
|
|
|
|
};
|
|
|
|
|
|
|
|
// Save `self` to `self.store`.
|
|
|
|
self.persist()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-01 02:36:10 +00:00
|
|
|
/// Returns a read-lock guarded `BeaconState` which is the `canonical_head` that has been
|
|
|
|
/// updated to match the current slot clock.
|
|
|
|
pub fn current_state(&self) -> RwLockReadGuard<BeaconState<T::EthSpec>> {
|
|
|
|
self.state.read()
|
|
|
|
}
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
/// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the
|
|
|
|
/// fork-choice rule).
|
|
|
|
///
|
|
|
|
/// It is important to note that the `beacon_state` returned may not match the present slot. It
|
|
|
|
/// is the state as it was when the head block was received, which could be some slots prior to
|
|
|
|
/// now.
|
|
|
|
pub fn head(&self) -> RwLockReadGuard<CheckPoint<T::EthSpec>> {
|
|
|
|
self.canonical_head.read()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the slot of the highest block in the canonical chain.
|
|
|
|
pub fn best_slot(&self) -> Slot {
|
|
|
|
self.canonical_head.read().beacon_block.slot
|
|
|
|
}
|
2019-03-24 05:35:07 +00:00
|
|
|
|
|
|
|
/// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`.
|
|
|
|
pub fn catchup_state(&self) -> Result<(), Error> {
|
2019-05-31 07:32:20 +00:00
|
|
|
let spec = &T::EthSpec::spec();
|
|
|
|
|
2019-03-24 05:35:07 +00:00
|
|
|
let present_slot = match self.slot_clock.present_slot() {
|
|
|
|
Ok(Some(slot)) => slot,
|
|
|
|
_ => return Err(Error::UnableToReadSlot),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut state = self.state.write();
|
|
|
|
|
|
|
|
// If required, transition the new state to the present slot.
|
|
|
|
for _ in state.slot.as_u64()..present_slot.as_u64() {
|
2019-03-26 23:34:52 +00:00
|
|
|
// Ensure the next epoch state caches are built in case of an epoch transition.
|
2019-06-03 23:25:00 +00:00
|
|
|
state.build_committee_cache(RelativeEpoch::Next, spec)?;
|
2019-03-26 23:34:52 +00:00
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
per_slot_processing(&mut *state, spec)?;
|
2019-03-24 05:35:07 +00:00
|
|
|
}
|
2019-03-31 02:40:12 +00:00
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
state.build_all_caches(spec)?;
|
2019-03-31 02:40:12 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build all of the caches on the current state.
|
|
|
|
///
|
|
|
|
/// Ideally this shouldn't be required, however we leave it here for testing.
|
|
|
|
pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> {
|
2019-05-31 07:32:20 +00:00
|
|
|
self.state.write().build_all_caches(&T::EthSpec::spec())?;
|
2019-03-24 05:35:07 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
/// Returns the validator index (if any) for the given public key.
|
|
|
|
///
|
|
|
|
/// Information is retrieved from the present `beacon_state.validator_registry`.
|
|
|
|
pub fn validator_index(&self, pubkey: &PublicKey) -> Option<usize> {
|
|
|
|
for (i, validator) in self
|
|
|
|
.head()
|
|
|
|
.beacon_state
|
|
|
|
.validator_registry
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
{
|
|
|
|
if validator.pubkey == *pubkey {
|
|
|
|
return Some(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads the slot clock, returns `None` if the slot is unavailable.
|
|
|
|
///
|
|
|
|
/// The slot might be unavailable due to an error with the system clock, or if the present time
|
|
|
|
/// is before genesis (i.e., a negative slot).
|
|
|
|
///
|
|
|
|
/// This is distinct to `present_slot`, which simply reads the latest state. If a
|
|
|
|
/// call to `read_slot_clock` results in a higher slot than a call to `present_slot`,
|
|
|
|
/// `self.state` should undergo per slot processing.
|
|
|
|
pub fn read_slot_clock(&self) -> Option<Slot> {
|
|
|
|
match self.slot_clock.present_slot() {
|
|
|
|
Ok(Some(some_slot)) => Some(some_slot),
|
|
|
|
Ok(None) => None,
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-26 23:36:20 +00:00
|
|
|
/// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since
|
|
|
|
/// genesis.
|
|
|
|
pub fn slots_since_genesis(&self) -> Option<SlotHeight> {
|
|
|
|
let now = self.read_slot_clock()?;
|
2019-05-31 07:32:20 +00:00
|
|
|
let genesis_slot = T::EthSpec::spec().genesis_slot;
|
2019-03-26 23:36:20 +00:00
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
if now < genesis_slot {
|
2019-03-26 23:36:20 +00:00
|
|
|
None
|
|
|
|
} else {
|
2019-05-31 07:32:20 +00:00
|
|
|
Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64()))
|
2019-03-26 23:36:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
/// Returns slot of the present state.
|
|
|
|
///
|
|
|
|
/// This is distinct to `read_slot_clock`, which reads from the actual system clock. If
|
|
|
|
/// `self.state` has not been transitioned it is possible for the system clock to be on a
|
|
|
|
/// different slot to what is returned from this call.
|
|
|
|
pub fn present_slot(&self) -> Slot {
|
|
|
|
self.state.read().slot
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the block proposer for a given slot.
|
|
|
|
///
|
|
|
|
/// Information is read from the present `beacon_state` shuffling, so only information from the
|
|
|
|
/// present and prior epoch is available.
|
2019-02-15 05:12:24 +00:00
|
|
|
pub fn block_proposer(&self, slot: Slot) -> Result<usize, BeaconStateError> {
|
2019-03-21 20:11:04 +00:00
|
|
|
self.state
|
|
|
|
.write()
|
2019-06-03 23:25:00 +00:00
|
|
|
.build_committee_cache(RelativeEpoch::Current, &T::EthSpec::spec())?;
|
2019-03-21 20:11:04 +00:00
|
|
|
|
2019-03-17 07:10:20 +00:00
|
|
|
let index = self.state.read().get_beacon_proposer_index(
|
|
|
|
slot,
|
|
|
|
RelativeEpoch::Current,
|
2019-05-31 07:32:20 +00:00
|
|
|
&T::EthSpec::spec(),
|
2019-03-17 07:10:20 +00:00
|
|
|
)?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the attestation slot and shard for a given validator index.
|
|
|
|
///
|
|
|
|
/// Information is read from the current state, so only information from the present and prior
|
|
|
|
/// epoch is available.
|
|
|
|
pub fn validator_attestion_slot_and_shard(
|
|
|
|
&self,
|
|
|
|
validator_index: usize,
|
2019-02-15 05:12:24 +00:00
|
|
|
) -> Result<Option<(Slot, u64)>, BeaconStateError> {
|
2019-02-15 08:23:22 +00:00
|
|
|
trace!(
|
|
|
|
"BeaconChain::validator_attestion_slot_and_shard: validator_index: {}",
|
|
|
|
validator_index
|
|
|
|
);
|
2019-03-17 07:10:20 +00:00
|
|
|
if let Some(attestation_duty) = self
|
2019-02-22 05:14:16 +00:00
|
|
|
.state
|
2019-02-14 01:09:18 +00:00
|
|
|
.read()
|
2019-06-03 06:13:51 +00:00
|
|
|
.get_attestation_duties(validator_index, RelativeEpoch::Current)?
|
2019-02-14 01:09:18 +00:00
|
|
|
{
|
2019-03-17 07:10:20 +00:00
|
|
|
Ok(Some((attestation_duty.slot, attestation_duty.shard)))
|
2019-02-14 01:09:18 +00:00
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Produce an `AttestationData` that is valid for the present `slot` and given `shard`.
|
2019-03-30 05:02:09 +00:00
|
|
|
pub fn produce_attestation_data(&self, shard: u64) -> Result<AttestationData, Error> {
|
2019-05-31 07:32:20 +00:00
|
|
|
let slots_per_epoch = T::EthSpec::spec().slots_per_epoch;
|
2019-05-29 07:52:43 +00:00
|
|
|
|
2019-05-29 05:45:09 +00:00
|
|
|
self.metrics.attestation_production_requests.inc();
|
2019-05-29 07:52:43 +00:00
|
|
|
let timer = self.metrics.attestation_production_times.start_timer();
|
2019-05-29 05:45:09 +00:00
|
|
|
|
2019-03-30 06:12:43 +00:00
|
|
|
let state = self.state.read();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-03-31 04:35:27 +00:00
|
|
|
let current_epoch_start_slot = self
|
|
|
|
.state
|
|
|
|
.read()
|
|
|
|
.slot
|
2019-05-31 07:32:20 +00:00
|
|
|
.epoch(slots_per_epoch)
|
|
|
|
.start_slot(slots_per_epoch);
|
2019-03-31 04:35:27 +00:00
|
|
|
|
|
|
|
let target_root = if state.slot == current_epoch_start_slot {
|
|
|
|
// If we're on the first slot of the state's epoch.
|
|
|
|
if self.head().beacon_block.slot == state.slot {
|
|
|
|
// If the current head block is from the current slot, use its block root.
|
|
|
|
self.head().beacon_block_root
|
|
|
|
} else {
|
|
|
|
// If the current head block is not from this slot, use the slot from the previous
|
|
|
|
// epoch.
|
2019-05-08 08:18:17 +00:00
|
|
|
*self
|
|
|
|
.state
|
|
|
|
.read()
|
2019-05-31 07:32:20 +00:00
|
|
|
.get_block_root(current_epoch_start_slot - slots_per_epoch)?
|
2019-03-31 04:35:27 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we're not on the first slot of the epoch.
|
2019-05-08 08:18:17 +00:00
|
|
|
*self.state.read().get_block_root(current_epoch_start_slot)?
|
2019-03-31 04:35:27 +00:00
|
|
|
};
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-06-03 06:13:51 +00:00
|
|
|
let previous_crosslink_root =
|
|
|
|
Hash256::from_slice(&state.get_current_crosslink(shard)?.tree_hash_root());
|
|
|
|
|
2019-05-29 05:45:09 +00:00
|
|
|
self.metrics.attestation_production_successes.inc();
|
|
|
|
timer.observe_duration();
|
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
Ok(AttestationData {
|
|
|
|
beacon_block_root: self.head().beacon_block_root,
|
2019-03-30 06:12:43 +00:00
|
|
|
source_epoch: state.current_justified_epoch,
|
|
|
|
source_root: state.current_justified_root,
|
2019-06-03 06:13:51 +00:00
|
|
|
target_epoch: state.current_epoch(),
|
|
|
|
target_root,
|
|
|
|
shard,
|
|
|
|
previous_crosslink_root,
|
|
|
|
crosslink_data_root: Hash256::zero(),
|
2019-02-14 01:09:18 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-03-29 07:40:50 +00:00
|
|
|
/// Accept a new attestation from the network.
|
|
|
|
///
|
|
|
|
/// If valid, the attestation is added to the `op_pool` and aggregated with another attestation
|
|
|
|
/// if possible.
|
2019-03-29 07:54:01 +00:00
|
|
|
pub fn process_attestation(
|
|
|
|
&self,
|
|
|
|
attestation: Attestation,
|
|
|
|
) -> Result<(), AttestationValidationError> {
|
2019-05-29 07:52:43 +00:00
|
|
|
self.metrics.attestation_processing_requests.inc();
|
|
|
|
let timer = self.metrics.attestation_processing_times.start_timer();
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
let result =
|
|
|
|
self.op_pool
|
|
|
|
.insert_attestation(attestation, &*self.state.read(), &T::EthSpec::spec());
|
2019-05-29 07:52:43 +00:00
|
|
|
|
|
|
|
if result.is_ok() {
|
2019-06-04 07:10:25 +00:00
|
|
|
self.metrics.attestation_processing_successes.inc();
|
2019-05-29 07:52:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
timer.observe_duration();
|
|
|
|
|
|
|
|
result
|
2019-03-29 07:40:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 00:54:51 +00:00
|
|
|
/// Accept some deposit and queue it for inclusion in an appropriate block.
|
2019-03-29 08:09:01 +00:00
|
|
|
pub fn process_deposit(
|
2019-03-29 07:54:01 +00:00
|
|
|
&self,
|
|
|
|
deposit: Deposit,
|
|
|
|
) -> Result<DepositInsertStatus, DepositValidationError> {
|
|
|
|
self.op_pool
|
2019-05-31 07:32:20 +00:00
|
|
|
.insert_deposit(deposit, &*self.state.read(), &T::EthSpec::spec())
|
2019-03-01 05:54:59 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 01:20:59 +00:00
|
|
|
/// Accept some exit and queue it for inclusion in an appropriate block.
|
2019-03-29 08:09:01 +00:00
|
|
|
pub fn process_voluntary_exit(&self, exit: VoluntaryExit) -> Result<(), ExitValidationError> {
|
2019-03-29 07:54:01 +00:00
|
|
|
self.op_pool
|
2019-05-31 07:32:20 +00:00
|
|
|
.insert_voluntary_exit(exit, &*self.state.read(), &T::EthSpec::spec())
|
2019-03-04 01:20:59 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 06:23:11 +00:00
|
|
|
/// Accept some transfer and queue it for inclusion in an appropriate block.
|
2019-03-29 08:09:01 +00:00
|
|
|
pub fn process_transfer(&self, transfer: Transfer) -> Result<(), TransferValidationError> {
|
2019-03-29 07:54:01 +00:00
|
|
|
self.op_pool
|
2019-05-31 07:32:20 +00:00
|
|
|
.insert_transfer(transfer, &*self.state.read(), &T::EthSpec::spec())
|
2019-03-07 06:23:11 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 00:54:51 +00:00
|
|
|
/// Accept some proposer slashing and queue it for inclusion in an appropriate block.
|
2019-03-29 08:09:01 +00:00
|
|
|
pub fn process_proposer_slashing(
|
2019-03-29 07:54:01 +00:00
|
|
|
&self,
|
|
|
|
proposer_slashing: ProposerSlashing,
|
|
|
|
) -> Result<(), ProposerSlashingValidationError> {
|
2019-05-31 07:32:20 +00:00
|
|
|
self.op_pool.insert_proposer_slashing(
|
|
|
|
proposer_slashing,
|
|
|
|
&*self.state.read(),
|
|
|
|
&T::EthSpec::spec(),
|
|
|
|
)
|
2019-03-02 00:23:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 00:54:51 +00:00
|
|
|
/// Accept some attester slashing and queue it for inclusion in an appropriate block.
|
2019-03-29 08:09:01 +00:00
|
|
|
pub fn process_attester_slashing(
|
2019-03-29 07:54:01 +00:00
|
|
|
&self,
|
|
|
|
attester_slashing: AttesterSlashing,
|
|
|
|
) -> Result<(), AttesterSlashingValidationError> {
|
2019-05-31 07:32:20 +00:00
|
|
|
self.op_pool.insert_attester_slashing(
|
|
|
|
attester_slashing,
|
|
|
|
&*self.state.read(),
|
|
|
|
&T::EthSpec::spec(),
|
|
|
|
)
|
2019-03-02 00:23:37 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
/// Accept some block and attempt to add it to block DAG.
|
|
|
|
///
|
|
|
|
/// Will accept blocks from prior slots, however it will reject any block from a future slot.
|
|
|
|
pub fn process_block(&self, block: BeaconBlock) -> Result<BlockProcessingOutcome, Error> {
|
2019-03-17 07:10:20 +00:00
|
|
|
debug!("Processing block with slot {}...", block.slot);
|
2019-05-29 05:45:09 +00:00
|
|
|
self.metrics.block_processing_requests.inc();
|
2019-05-29 07:52:43 +00:00
|
|
|
let timer = self.metrics.block_processing_times.start_timer();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-03-19 23:51:53 +00:00
|
|
|
let block_root = block.block_header().canonical_root();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
let present_slot = self.present_slot();
|
|
|
|
|
|
|
|
if block.slot > present_slot {
|
|
|
|
return Ok(BlockProcessingOutcome::InvalidBlock(
|
2019-03-24 05:35:07 +00:00
|
|
|
InvalidBlock::FutureSlot {
|
|
|
|
present_slot,
|
|
|
|
block_slot: block.slot,
|
|
|
|
},
|
2019-02-14 01:09:18 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the blocks parent block from the database, returning invalid if that block is not
|
|
|
|
// found.
|
2019-03-17 07:10:20 +00:00
|
|
|
let parent_block_root = block.previous_block_root;
|
2019-05-21 07:27:06 +00:00
|
|
|
let parent_block: BeaconBlock = match self.store.get(&parent_block_root)? {
|
2019-03-17 07:10:20 +00:00
|
|
|
Some(previous_block_root) => previous_block_root,
|
2019-02-14 01:09:18 +00:00
|
|
|
None => {
|
|
|
|
return Ok(BlockProcessingOutcome::InvalidBlock(
|
|
|
|
InvalidBlock::ParentUnknown,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Load the parent blocks state from the database, returning an error if it is not found.
|
|
|
|
// It is an error because if know the parent block we should also know the parent state.
|
2019-03-17 07:10:20 +00:00
|
|
|
let parent_state_root = parent_block.state_root;
|
2019-02-14 01:09:18 +00:00
|
|
|
let parent_state = self
|
2019-05-21 07:27:06 +00:00
|
|
|
.store
|
|
|
|
.get(&parent_state_root)?
|
2019-03-17 07:10:20 +00:00
|
|
|
.ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
// TODO: check the block proposer signature BEFORE doing a state transition. This will
|
|
|
|
// significantly lower exposure surface to DoS attacks.
|
|
|
|
|
2019-03-23 07:48:09 +00:00
|
|
|
// Transition the parent state to the block slot.
|
2019-05-25 10:51:15 +00:00
|
|
|
let mut state: BeaconState<T::EthSpec> = parent_state;
|
2019-03-23 07:48:09 +00:00
|
|
|
for _ in state.slot.as_u64()..block.slot.as_u64() {
|
2019-05-31 07:32:20 +00:00
|
|
|
if let Err(e) = per_slot_processing(&mut state, &T::EthSpec::spec()) {
|
2019-02-14 01:09:18 +00:00
|
|
|
return Ok(BlockProcessingOutcome::InvalidBlock(
|
|
|
|
InvalidBlock::SlotProcessingError(e),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-04 03:13:38 +00:00
|
|
|
state.build_committee_cache(RelativeEpoch::Current, &T::EthSpec::spec())?;
|
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
// Apply the received block to its parent state (which has been transitioned into this
|
|
|
|
// slot).
|
2019-05-31 07:32:20 +00:00
|
|
|
if let Err(e) = per_block_processing(&mut state, &block, &T::EthSpec::spec()) {
|
2019-02-14 01:09:18 +00:00
|
|
|
return Ok(BlockProcessingOutcome::InvalidBlock(
|
|
|
|
InvalidBlock::PerBlockProcessingError(e),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let state_root = state.canonical_root();
|
|
|
|
|
|
|
|
if block.state_root != state_root {
|
|
|
|
return Ok(BlockProcessingOutcome::InvalidBlock(
|
|
|
|
InvalidBlock::StateRootMismatch,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the block and state.
|
2019-05-21 07:27:06 +00:00
|
|
|
self.store.put(&block_root, &block)?;
|
|
|
|
self.store.put(&state_root, &state)?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-29 07:55:38 +00:00
|
|
|
// Register the new block with the fork choice service.
|
2019-02-18 06:49:05 +00:00
|
|
|
self.fork_choice
|
|
|
|
.write()
|
2019-05-31 07:32:20 +00:00
|
|
|
.add_block(&block, &block_root, &T::EthSpec::spec())?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-29 07:55:38 +00:00
|
|
|
// Execute the fork choice algorithm, enthroning a new head if discovered.
|
2019-02-14 01:09:18 +00:00
|
|
|
//
|
2019-05-29 07:55:38 +00:00
|
|
|
// Note: in the future we may choose to run fork-choice less often, potentially based upon
|
|
|
|
// some heuristic around number of attestations seen for the block.
|
|
|
|
self.fork_choice()?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-29 05:45:09 +00:00
|
|
|
self.metrics.block_processing_successes.inc();
|
2019-06-03 23:25:00 +00:00
|
|
|
self.metrics
|
|
|
|
.operations_per_block_attestation
|
|
|
|
.observe(block.body.attestations.len() as f64);
|
2019-05-29 05:45:09 +00:00
|
|
|
timer.observe_duration();
|
2019-05-28 07:30:09 +00:00
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
Ok(BlockProcessingOutcome::ValidBlock(ValidBlock::Processed))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Produce a new block at the present slot.
|
|
|
|
///
|
|
|
|
/// The produced block will not be inherently valid, it must be signed by a block producer.
|
|
|
|
/// Block signing is out of the scope of this function and should be done by a separate program.
|
2019-03-07 01:53:15 +00:00
|
|
|
pub fn produce_block(
|
|
|
|
&self,
|
|
|
|
randao_reveal: Signature,
|
2019-05-25 10:51:15 +00:00
|
|
|
) -> Result<(BeaconBlock, BeaconState<T::EthSpec>), BlockProductionError> {
|
2019-02-14 01:09:18 +00:00
|
|
|
debug!("Producing block at slot {}...", self.state.read().slot);
|
2019-05-28 07:30:09 +00:00
|
|
|
self.metrics.block_production_requests.inc();
|
2019-05-29 07:52:43 +00:00
|
|
|
let timer = self.metrics.block_production_times.start_timer();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
let mut state = self.state.read().clone();
|
|
|
|
|
2019-06-03 23:37:40 +00:00
|
|
|
state.build_committee_cache(RelativeEpoch::Current, &T::EthSpec::spec())?;
|
2019-03-21 20:11:04 +00:00
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
trace!("Finding attestations for new block...");
|
|
|
|
|
2019-03-17 07:10:20 +00:00
|
|
|
let previous_block_root = *state
|
2019-05-08 08:18:17 +00:00
|
|
|
.get_block_root(state.slot - 1)
|
2019-03-17 07:10:20 +00:00
|
|
|
.map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
let (proposer_slashings, attester_slashings) = self
|
|
|
|
.op_pool
|
|
|
|
.get_slashings(&*self.state.read(), &T::EthSpec::spec());
|
2019-03-29 07:30:03 +00:00
|
|
|
|
2019-02-14 01:09:18 +00:00
|
|
|
let mut block = BeaconBlock {
|
|
|
|
slot: state.slot,
|
2019-03-17 07:10:20 +00:00
|
|
|
previous_block_root,
|
2019-02-14 01:09:18 +00:00
|
|
|
state_root: Hash256::zero(), // Updated after the state is calculated.
|
2019-06-03 23:37:40 +00:00
|
|
|
signature: Signature::empty_signature(), // To be completed by a validator.
|
2019-02-14 01:09:18 +00:00
|
|
|
body: BeaconBlockBody {
|
2019-03-17 07:10:20 +00:00
|
|
|
randao_reveal,
|
|
|
|
eth1_data: Eth1Data {
|
|
|
|
// TODO: replace with real data
|
2019-06-03 06:13:51 +00:00
|
|
|
deposit_count: 0,
|
2019-03-17 07:10:20 +00:00
|
|
|
deposit_root: Hash256::zero(),
|
|
|
|
block_hash: Hash256::zero(),
|
|
|
|
},
|
2019-06-03 06:13:51 +00:00
|
|
|
// TODO: badass Lighthouse graffiti
|
|
|
|
graffiti: [0; 32],
|
2019-03-29 07:30:03 +00:00
|
|
|
proposer_slashings,
|
|
|
|
attester_slashings,
|
2019-03-30 01:26:25 +00:00
|
|
|
attestations: self
|
2019-03-29 07:30:03 +00:00
|
|
|
.op_pool
|
2019-05-31 07:32:20 +00:00
|
|
|
.get_attestations(&*self.state.read(), &T::EthSpec::spec()),
|
|
|
|
deposits: self
|
|
|
|
.op_pool
|
|
|
|
.get_deposits(&*self.state.read(), &T::EthSpec::spec()),
|
2019-03-29 07:30:03 +00:00
|
|
|
voluntary_exits: self
|
|
|
|
.op_pool
|
2019-05-31 07:32:20 +00:00
|
|
|
.get_voluntary_exits(&*self.state.read(), &T::EthSpec::spec()),
|
|
|
|
transfers: self
|
|
|
|
.op_pool
|
|
|
|
.get_transfers(&*self.state.read(), &T::EthSpec::spec()),
|
2019-02-14 01:09:18 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-03-30 06:13:23 +00:00
|
|
|
debug!(
|
|
|
|
"Produced block with {} attestations, updating state.",
|
|
|
|
block.body.attestations.len()
|
|
|
|
);
|
2019-02-16 00:04:12 +00:00
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
per_block_processing_without_verifying_block_signature(
|
|
|
|
&mut state,
|
|
|
|
&block,
|
|
|
|
&T::EthSpec::spec(),
|
|
|
|
)?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
let state_root = state.canonical_root();
|
|
|
|
|
|
|
|
block.state_root = state_root;
|
|
|
|
|
2019-05-28 07:30:09 +00:00
|
|
|
self.metrics.block_production_successes.inc();
|
2019-05-29 05:45:09 +00:00
|
|
|
timer.observe_duration();
|
2019-05-28 07:30:09 +00:00
|
|
|
|
2019-03-07 01:53:15 +00:00
|
|
|
Ok((block, state))
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-29 07:52:43 +00:00
|
|
|
/// Execute the fork choice algorithm and enthrone the result as the canonical head.
|
2019-02-14 01:09:18 +00:00
|
|
|
pub fn fork_choice(&self) -> Result<(), Error> {
|
2019-05-29 07:52:43 +00:00
|
|
|
self.metrics.fork_choice_requests.inc();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
// Start fork choice metrics timer.
|
2019-05-29 07:52:43 +00:00
|
|
|
let timer = self.metrics.fork_choice_times.start_timer();
|
|
|
|
|
2019-06-01 05:02:19 +00:00
|
|
|
let justified_root = {
|
|
|
|
let root = self.head().beacon_state.current_justified_root;
|
|
|
|
if root == T::EthSpec::spec().zero_hash {
|
2019-06-03 23:25:00 +00:00
|
|
|
self.genesis_block_root
|
2019-06-01 05:02:19 +00:00
|
|
|
} else {
|
|
|
|
root
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
// Determine the root of the block that is the head of the chain.
|
|
|
|
let beacon_block_root = self
|
2019-02-18 06:49:05 +00:00
|
|
|
.fork_choice
|
|
|
|
.write()
|
2019-06-01 05:02:19 +00:00
|
|
|
.find_head(&justified_root, &T::EthSpec::spec())?;
|
2019-05-29 07:52:43 +00:00
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
// End fork choice metrics timer.
|
2019-05-29 07:52:43 +00:00
|
|
|
timer.observe_duration();
|
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
// If a new head was chosen.
|
|
|
|
if beacon_block_root != self.head().beacon_block_root {
|
2019-05-29 07:52:43 +00:00
|
|
|
self.metrics.fork_choice_changed_head.inc();
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
let beacon_block: BeaconBlock = self
|
2019-05-21 07:27:06 +00:00
|
|
|
.store
|
2019-05-31 07:51:32 +00:00
|
|
|
.get(&beacon_block_root)?
|
|
|
|
.ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?;
|
|
|
|
|
|
|
|
let beacon_state_root = beacon_block.state_root;
|
|
|
|
let beacon_state: BeaconState<T::EthSpec> = self
|
2019-05-21 07:27:06 +00:00
|
|
|
.store
|
2019-05-31 07:51:32 +00:00
|
|
|
.get(&beacon_state_root)?
|
|
|
|
.ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?;
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-05-31 07:51:32 +00:00
|
|
|
// If we switched to a new chain (instead of building atop the present chain).
|
|
|
|
if self.head().beacon_block_root != beacon_block.previous_block_root {
|
2019-05-29 07:52:43 +00:00
|
|
|
self.metrics.fork_choice_reorg_count.inc();
|
|
|
|
};
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
self.update_canonical_head(CheckPoint {
|
2019-05-31 07:51:32 +00:00
|
|
|
beacon_block,
|
|
|
|
beacon_block_root,
|
|
|
|
beacon_state,
|
|
|
|
beacon_state_root,
|
2019-05-31 07:32:20 +00:00
|
|
|
})?;
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-03-18 05:53:59 +00:00
|
|
|
|
2019-03-29 08:09:01 +00:00
|
|
|
/// Returns `true` if the given block root has not been processed.
|
|
|
|
pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result<bool, Error> {
|
2019-05-21 07:27:06 +00:00
|
|
|
Ok(!self.store.exists::<BeaconBlock>(beacon_block_root)?)
|
2019-03-29 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
2019-03-18 05:53:59 +00:00
|
|
|
/// Dumps the entire canonical chain, from the head to genesis to a vector for analysis.
|
|
|
|
///
|
|
|
|
/// This could be a very expensive operation and should only be done in testing/analysis
|
|
|
|
/// activities.
|
2019-05-25 10:51:15 +00:00
|
|
|
pub fn chain_dump(&self) -> Result<Vec<CheckPoint<T::EthSpec>>, Error> {
|
2019-03-18 05:53:59 +00:00
|
|
|
let mut dump = vec![];
|
|
|
|
|
|
|
|
let mut last_slot = CheckPoint {
|
|
|
|
beacon_block: self.head().beacon_block.clone(),
|
|
|
|
beacon_block_root: self.head().beacon_block_root,
|
|
|
|
beacon_state: self.head().beacon_state.clone(),
|
|
|
|
beacon_state_root: self.head().beacon_state_root,
|
|
|
|
};
|
|
|
|
|
|
|
|
dump.push(last_slot.clone());
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let beacon_block_root = last_slot.beacon_block.previous_block_root;
|
|
|
|
|
2019-05-31 07:32:20 +00:00
|
|
|
if beacon_block_root == T::EthSpec::spec().zero_hash {
|
2019-03-18 05:53:59 +00:00
|
|
|
break; // Genesis has been reached.
|
|
|
|
}
|
|
|
|
|
2019-05-21 07:27:06 +00:00
|
|
|
let beacon_block: BeaconBlock =
|
|
|
|
self.store.get(&beacon_block_root)?.ok_or_else(|| {
|
2019-03-18 05:53:59 +00:00
|
|
|
Error::DBInconsistent(format!("Missing block {}", beacon_block_root))
|
|
|
|
})?;
|
|
|
|
let beacon_state_root = beacon_block.state_root;
|
2019-05-21 07:27:06 +00:00
|
|
|
let beacon_state = self.store.get(&beacon_state_root)?.ok_or_else(|| {
|
|
|
|
Error::DBInconsistent(format!("Missing state {}", beacon_state_root))
|
|
|
|
})?;
|
2019-03-18 05:53:59 +00:00
|
|
|
|
|
|
|
let slot = CheckPoint {
|
|
|
|
beacon_block,
|
|
|
|
beacon_block_root,
|
|
|
|
beacon_state,
|
|
|
|
beacon_state_root,
|
|
|
|
};
|
|
|
|
|
|
|
|
dump.push(slot.clone());
|
|
|
|
last_slot = slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump.reverse();
|
|
|
|
|
|
|
|
Ok(dump)
|
|
|
|
}
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DBError> for Error {
|
|
|
|
fn from(e: DBError) -> Error {
|
2019-05-21 07:27:06 +00:00
|
|
|
Error::DBError(e)
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ForkChoiceError> for Error {
|
|
|
|
fn from(e: ForkChoiceError) -> Error {
|
|
|
|
Error::ForkChoiceError(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-15 05:12:24 +00:00
|
|
|
impl From<BeaconStateError> for Error {
|
|
|
|
fn from(e: BeaconStateError) -> Error {
|
|
|
|
Error::BeaconStateError(e)
|
2019-02-14 01:09:18 +00:00
|
|
|
}
|
|
|
|
}
|