diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 44e6cda01..1db739d7c 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -13,8 +13,12 @@ clap = "2.32.0" db = { path = "db" } dirs = "1.0.3" futures = "0.1.23" +genesis = { path = "../eth2/genesis" } slog = "^2.2.3" +slot_clock = { path = "../eth2/utils/slot_clock" } slog-term = "^2.4.0" slog-async = "^2.3.0" +spec = { path = "../eth2/spec" } +types = { path = "../eth2/types" } ssz = { path = "../eth2/utils/ssz" } tokio = "0.1" diff --git a/beacon_node/src/beacon_chain/tests/chain_test.rs b/beacon_node/src/beacon_chain/chain_test.rs similarity index 96% rename from beacon_node/src/beacon_chain/tests/chain_test.rs rename to beacon_node/src/beacon_chain/chain_test.rs index 8d2cfa501..3d7d54d36 100644 --- a/beacon_node/src/beacon_chain/tests/chain_test.rs +++ b/beacon_node/src/beacon_chain/chain_test.rs @@ -1,4 +1,4 @@ -use chain::{BeaconChain, BlockProcessingOutcome}; +use super::{BeaconChain, BlockProcessingOutcome}; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, MemoryDB, diff --git a/beacon_node/src/beacon_chain/lmd_ghost.rs b/beacon_node/src/beacon_chain/lmd_ghost.rs new file mode 100644 index 000000000..7f00a9276 --- /dev/null +++ b/beacon_node/src/beacon_chain/lmd_ghost.rs @@ -0,0 +1,170 @@ +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 BeaconChain +where + T: ClientDB, + U: SlotClock, + Error: From<::Error>, +{ + pub fn slow_lmd_ghost(&mut self, start_hash: &Hash256) -> Result { + 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( + block_store: &Arc>, + attestation_targets: &[&Hash256], + block_root: &Hash256, + slot: u64, +) -> Result { + 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( + block_store: &Arc>, + root_hash: &Hash256, + leaf_hashes: &HashSet, +) -> Result, 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 for Error { + fn from(e: DBError) -> Error { + Error::DBError(e.message) + } +} + +impl From 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 for Error { + fn from(_: TestingSlotClockError) -> Error { + unreachable!(); // Testing clock never throws an error. + } +} diff --git a/beacon_node/src/beacon_chain/mod.rs b/beacon_node/src/beacon_chain/mod.rs index 2a3d41538..be1d6ce9d 100644 --- a/beacon_node/src/beacon_chain/mod.rs +++ b/beacon_node/src/beacon_chain/mod.rs @@ -1,5 +1,8 @@ mod block_processing; mod block_production; +#[cfg(test)] +mod chain_test; +mod lmd_ghost; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, @@ -9,11 +12,18 @@ use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError}; use slot_clock::SlotClock; use spec::ChainSpec; use ssz::ssz_encode; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use types::Hash256; -pub use crate::block_processing::Outcome as BlockProcessingOutcome; +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 { @@ -29,6 +39,8 @@ pub struct BeaconChain { pub leaf_blocks: HashSet, pub canonical_leaf_block: Hash256, pub spec: ChainSpec, + latest_attestation_targets: HashMap, + finalized_checkpoint: CheckPoint, } impl BeaconChain @@ -57,6 +69,12 @@ where 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, @@ -64,6 +82,8 @@ where leaf_blocks, canonical_leaf_block: block_root, spec, + latest_attestation_targets: HashMap::new(), + finalized_checkpoint, }) } } diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 08660cbc4..26b6e362e 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -1,14 +1,23 @@ 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 clap::{App, Arg}; +use db::{ + stores::{BeaconBlockStore, BeaconStateStore}, + MemoryDB, +}; use slog::{error, info, o, Drain}; +use slot_clock::SystemTimeSlotClock; +use spec::ChainSpec; +use std::sync::Arc; fn main() { let decorator = slog_term::TermDecorator::new().build(); @@ -58,6 +67,23 @@ fn main() { "data_dir" => &config.data_dir.to_str(), "port" => &config.p2p_listen_port); + // Specification (presently fixed to foundation). + let spec = ChainSpec::foundation(); + + // Database (presently in-memory) + let db = Arc::new(MemoryDB::open()); + let block_store = Arc::new(BeaconBlockStore::new(db.clone())); + let state_store = Arc::new(BeaconStateStore::new(db.clone())); + + // Slot clock + let slot_clock = SystemTimeSlotClock::new(spec.genesis_time, spec.slot_duration) + .expect("Unable to load SystemTimeSlotClock"); + + // Genesis chain + // TODO: persist chain to storage. + let _chain_result = + BeaconChain::genesis(state_store.clone(), block_store.clone(), slot_clock, spec); + let _server = start_server(log.clone()); loop { diff --git a/eth2/types/src/readers/block_reader.rs b/eth2/types/src/readers/block_reader.rs index 91a2852ac..d87bc5caf 100644 --- a/eth2/types/src/readers/block_reader.rs +++ b/eth2/types/src/readers/block_reader.rs @@ -1,3 +1,4 @@ +use super::state_reader::BeaconStateReader; use crate::{BeaconBlock, Hash256}; use std::fmt::Debug;