mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-11 18:04:18 +00:00
Implement very raw state transition logic
This commit is contained in:
@@ -1,72 +0,0 @@
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use types::{readers::BeaconBlockReader, Hash256};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Outcome {
|
||||
FutureSlot,
|
||||
Processed,
|
||||
|
||||
NewCanonicalBlock,
|
||||
NewReorgBlock,
|
||||
NewForkBlock,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
NotImplemented,
|
||||
PresentSlotIsNone,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn process_block<V>(&mut self, block: &V) -> Result<(Outcome, Hash256), Error>
|
||||
where
|
||||
V: BeaconBlockReader + Encodable + Sized,
|
||||
{
|
||||
let block_root = block.canonical_root();
|
||||
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
|
||||
// Block from future slots (i.e., greater than the present slot) should not be processed.
|
||||
if block.slot() > present_slot {
|
||||
return Ok((Outcome::FutureSlot, block_root));
|
||||
}
|
||||
|
||||
// TODO: block processing has been removed.
|
||||
// https://github.com/sigp/lighthouse/issues/98
|
||||
|
||||
// Update leaf blocks.
|
||||
self.block_store.put(&block_root, &ssz_encode(block)[..])?;
|
||||
if self.leaf_blocks.contains(&block.parent_root()) {
|
||||
self.leaf_blocks.remove(&block.parent_root());
|
||||
}
|
||||
if self.canonical_leaf_block == block.parent_root() {
|
||||
self.canonical_leaf_block = block_root;
|
||||
}
|
||||
self.leaf_blocks.insert(block_root);
|
||||
|
||||
Ok((Outcome::Processed, block_root))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
BeaconBlock, BeaconState, Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
PresentSlotIsNone,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn produce_block(&mut self) -> Result<(BeaconBlock, BeaconState), Error> {
|
||||
/*
|
||||
* Important: this code is a big stub and only exists to ensure that tests pass.
|
||||
*
|
||||
* https://github.com/sigp/lighthouse/issues/107
|
||||
*/
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
let parent_root = self.canonical_leaf_block;
|
||||
let parent_block_reader = self
|
||||
.block_store
|
||||
.get_reader(&parent_root)?
|
||||
.ok_or_else(|| Error::DBError("Block not found.".to_string()))?;
|
||||
let parent_state_reader = self
|
||||
.state_store
|
||||
.get_reader(&parent_block_reader.state_root())?
|
||||
.ok_or_else(|| Error::DBError("State not found.".to_string()))?;
|
||||
|
||||
let parent_block = parent_block_reader
|
||||
.into_beacon_block()
|
||||
.ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?;
|
||||
let mut block = BeaconBlock {
|
||||
slot: present_slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
..parent_block
|
||||
};
|
||||
|
||||
let parent_state = parent_state_reader
|
||||
.into_beacon_state()
|
||||
.ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?;
|
||||
let state = BeaconState {
|
||||
slot: present_slot,
|
||||
..parent_state
|
||||
};
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
block.state_root = state_root;
|
||||
|
||||
Ok((block, state))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
use super::{BeaconChain, BlockProcessingOutcome};
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
MemoryDB,
|
||||
};
|
||||
use slot_clock::TestingSlotClock;
|
||||
use spec::ChainSpec;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn in_memory_test_stores() -> (
|
||||
Arc<MemoryDB>,
|
||||
Arc<BeaconBlockStore<MemoryDB>>,
|
||||
Arc<BeaconStateStore<MemoryDB>>,
|
||||
) {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
|
||||
let state_store = Arc::new(BeaconStateStore::new(db.clone()));
|
||||
(db, block_store, state_store)
|
||||
}
|
||||
|
||||
fn in_memory_test_chain(
|
||||
spec: ChainSpec,
|
||||
) -> (Arc<MemoryDB>, BeaconChain<MemoryDB, TestingSlotClock>) {
|
||||
let (db, block_store, state_store) = in_memory_test_stores();
|
||||
let slot_clock = TestingSlotClock::new(0);
|
||||
|
||||
let chain = BeaconChain::genesis(state_store, block_store, slot_clock, spec);
|
||||
(db, chain.unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_constructs() {
|
||||
let (_db, _chain) = in_memory_test_chain(ChainSpec::foundation());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_produces() {
|
||||
let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation());
|
||||
let (_block, _state) = chain.produce_block().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_processes_a_block_it_produces() {
|
||||
let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation());
|
||||
let (block, _state) = chain.produce_block().unwrap();
|
||||
let (outcome, new_block_hash) = chain.process_block(&block).unwrap();
|
||||
assert_eq!(outcome, BlockProcessingOutcome::Processed);
|
||||
assert_eq!(chain.canonical_leaf_block, new_block_hash);
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
use super::{BeaconChain, SlotClock};
|
||||
use db::{
|
||||
stores::{BeaconBlockAtSlotError, BeaconBlockStore},
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
validator_registry::get_active_validator_indices,
|
||||
BeaconBlock, Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Outcome {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
MissingBeaconState(Hash256),
|
||||
InvalidBeaconState(Hash256),
|
||||
MissingBeaconBlock(Hash256),
|
||||
InvalidBeaconBlock(Hash256),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn slow_lmd_ghost(&mut self, start_hash: &Hash256) -> Result<Hash256, Error> {
|
||||
let start = self
|
||||
.block_store
|
||||
.get_reader(&start_hash)?
|
||||
.ok_or(Error::MissingBeaconBlock(*start_hash))?;
|
||||
|
||||
let start_state_root = start.state_root();
|
||||
|
||||
let state = self
|
||||
.state_store
|
||||
.get_reader(&start_state_root)?
|
||||
.ok_or(Error::MissingBeaconState(start_state_root))?
|
||||
.into_beacon_state()
|
||||
.ok_or(Error::InvalidBeaconState(start_state_root))?;
|
||||
|
||||
let active_validator_indices =
|
||||
get_active_validator_indices(&state.validator_registry, start.slot());
|
||||
|
||||
let mut attestation_targets = Vec::with_capacity(active_validator_indices.len());
|
||||
for i in active_validator_indices {
|
||||
if let Some(target) = self.latest_attestation_targets.get(&i) {
|
||||
attestation_targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
let mut head_hash = Hash256::zero();
|
||||
let mut head_vote_count = 0;
|
||||
|
||||
loop {
|
||||
let child_hashes_and_slots =
|
||||
get_child_hashes_and_slots(&self.block_store, &head_hash, &self.leaf_blocks)?;
|
||||
|
||||
if child_hashes_and_slots.len() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
for (child_hash, child_slot) in child_hashes_and_slots {
|
||||
let vote_count = get_vote_count(
|
||||
&self.block_store,
|
||||
&attestation_targets[..],
|
||||
&child_hash,
|
||||
child_slot,
|
||||
)?;
|
||||
|
||||
if vote_count > head_vote_count {
|
||||
head_hash = child_hash;
|
||||
head_vote_count = vote_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(head_hash)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vote_count<T: ClientDB>(
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
attestation_targets: &[&Hash256],
|
||||
block_root: &Hash256,
|
||||
slot: u64,
|
||||
) -> Result<u64, Error> {
|
||||
let mut count = 0;
|
||||
for target in attestation_targets {
|
||||
let (root_at_slot, _) = block_store
|
||||
.block_at_slot(&block_root, slot)?
|
||||
.ok_or(Error::MissingBeaconBlock(*block_root))?;
|
||||
if root_at_slot == *block_root {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Starting from some `leaf_hashes`, recurse back down each branch until the `root_hash`, adding
|
||||
/// each `block_root` and `slot` to a HashSet.
|
||||
fn get_child_hashes_and_slots<T: ClientDB>(
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
root_hash: &Hash256,
|
||||
leaf_hashes: &HashSet<Hash256>,
|
||||
) -> Result<HashSet<(Hash256, u64)>, Error> {
|
||||
let mut hash_set = HashSet::new();
|
||||
|
||||
for leaf_hash in leaf_hashes {
|
||||
let mut current_hash = *leaf_hash;
|
||||
|
||||
loop {
|
||||
if let Some(block_reader) = block_store.get_reader(¤t_hash)? {
|
||||
let parent_root = block_reader.parent_root();
|
||||
|
||||
let new_hash = hash_set.insert((current_hash, block_reader.slot()));
|
||||
|
||||
// If the hash just added was already in the set, break the loop.
|
||||
//
|
||||
// In such a case, the present branch has merged with a branch that is already in
|
||||
// the set.
|
||||
if !new_hash {
|
||||
break;
|
||||
}
|
||||
|
||||
// The branch is exhausted if the parent of this block is the root_hash.
|
||||
if parent_root == *root_hash {
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = parent_root.clone();
|
||||
} else {
|
||||
return Err(Error::MissingBeaconBlock(current_hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(hash_set)
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconBlockAtSlotError> for Error {
|
||||
fn from(e: BeaconBlockAtSlotError) -> Error {
|
||||
match e {
|
||||
BeaconBlockAtSlotError::UnknownBeaconBlock(h) => Error::MissingBeaconBlock(h),
|
||||
BeaconBlockAtSlotError::InvalidBeaconBlock(h) => Error::InvalidBeaconBlock(h),
|
||||
BeaconBlockAtSlotError::DBError(msg) => Error::DBError(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
mod block_processing;
|
||||
mod block_production;
|
||||
#[cfg(test)]
|
||||
mod chain_test;
|
||||
mod lmd_ghost;
|
||||
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError};
|
||||
use slot_clock::SlotClock;
|
||||
use spec::ChainSpec;
|
||||
use ssz::ssz_encode;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use types::Hash256;
|
||||
|
||||
pub use self::block_processing::Outcome as BlockProcessingOutcome;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CheckPoint {
|
||||
block_root: Hash256,
|
||||
state_root: Hash256,
|
||||
slot: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BeaconChainError {
|
||||
InsufficientValidators,
|
||||
GenesisError(GenesisError),
|
||||
DBError(String),
|
||||
}
|
||||
|
||||
pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock> {
|
||||
pub block_store: Arc<BeaconBlockStore<T>>,
|
||||
pub state_store: Arc<BeaconStateStore<T>>,
|
||||
pub slot_clock: U,
|
||||
pub leaf_blocks: HashSet<Hash256>,
|
||||
pub canonical_leaf_block: Hash256,
|
||||
pub spec: ChainSpec,
|
||||
latest_attestation_targets: HashMap<usize, Hash256>,
|
||||
finalized_checkpoint: CheckPoint,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn genesis(
|
||||
state_store: Arc<BeaconStateStore<T>>,
|
||||
block_store: Arc<BeaconBlockStore<T>>,
|
||||
slot_clock: U,
|
||||
spec: ChainSpec,
|
||||
) -> Result<Self, BeaconChainError> {
|
||||
if spec.initial_validators.is_empty() {
|
||||
return Err(BeaconChainError::InsufficientValidators);
|
||||
}
|
||||
|
||||
let genesis_state = genesis_beacon_state(&spec)?;
|
||||
let state_root = genesis_state.canonical_root();
|
||||
state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?;
|
||||
|
||||
let genesis_block = genesis_beacon_block(state_root, &spec);
|
||||
let block_root = genesis_block.canonical_root();
|
||||
block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?;
|
||||
|
||||
let mut leaf_blocks = HashSet::new();
|
||||
leaf_blocks.insert(block_root);
|
||||
|
||||
let finalized_checkpoint = CheckPoint {
|
||||
block_root,
|
||||
state_root,
|
||||
slot: genesis_block.slot,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
block_store,
|
||||
state_store,
|
||||
slot_clock,
|
||||
leaf_blocks,
|
||||
canonical_leaf_block: block_root,
|
||||
spec,
|
||||
latest_attestation_targets: HashMap::new(),
|
||||
finalized_checkpoint,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for BeaconChainError {
|
||||
fn from(e: DBError) -> BeaconChainError {
|
||||
BeaconChainError::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenesisError> for BeaconChainError {
|
||||
fn from(e: GenesisError) -> BeaconChainError {
|
||||
BeaconChainError::GenesisError(e)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use super::BeaconChain;
|
||||
use db::ClientDB;
|
||||
use state_transition::{extend_active_state, StateTransitionError};
|
||||
use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256};
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
extern crate slog;
|
||||
|
||||
mod beacon_chain;
|
||||
mod config;
|
||||
mod rpc;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use self::beacon_chain::BeaconChain;
|
||||
use crate::config::LighthouseConfig;
|
||||
use crate::rpc::start_server;
|
||||
use beacon_chain::BeaconChain;
|
||||
use clap::{App, Arg};
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
|
||||
Reference in New Issue
Block a user