Add slow LMD ghost implementation

This commit is contained in:
Paul Hauner 2019-01-23 09:33:04 +11:00
parent 1fabc7e0ab
commit b555916808
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
6 changed files with 224 additions and 3 deletions

View File

@ -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"

View File

@ -1,4 +1,4 @@
use chain::{BeaconChain, BlockProcessingOutcome};
use super::{BeaconChain, BlockProcessingOutcome};
use db::{
stores::{BeaconBlockStore, BeaconStateStore},
MemoryDB,

View File

@ -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<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(&current_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.
}
}

View File

@ -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<T: ClientDB + Sized, U: SlotClock> {
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>
@ -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,
})
}
}

View File

@ -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 {

View File

@ -1,3 +1,4 @@
use super::state_reader::BeaconStateReader;
use crate::{BeaconBlock, Hash256};
use std::fmt::Debug;