Remove block_graph from beacon_chain.

This commit is contained in:
Age Manning 2019-02-13 11:38:22 +11:00
parent 03a5a892d0
commit ef1717312f
No known key found for this signature in database
GPG Key ID: 05EED64B79E06A93
5 changed files with 40 additions and 254 deletions

View File

@ -17,7 +17,6 @@ use types::{
};
use crate::attestation_aggregator::{AttestationAggregator, Outcome as AggregationOutcome};
use crate::block_graph::BlockGraph;
use crate::checkpoint::CheckPoint;
#[derive(Debug, PartialEq)]
@ -65,7 +64,6 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock, F: ForkChoice> {
pub block_store: Arc<BeaconBlockStore<T>>,
pub state_store: Arc<BeaconStateStore<T>>,
pub slot_clock: U,
pub block_graph: BlockGraph,
pub attestation_aggregator: RwLock<AttestationAggregator>,
canonical_head: RwLock<CheckPoint>,
finalized_head: RwLock<CheckPoint>,
@ -101,9 +99,6 @@ where
let block_root = genesis_block.canonical_root();
block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?;
let block_graph = BlockGraph::new();
block_graph.add_leaf(&Hash256::zero(), block_root);
let finalized_head = RwLock::new(CheckPoint::new(
genesis_block.clone(),
block_root,
@ -128,7 +123,6 @@ where
block_store,
state_store,
slot_clock,
block_graph,
attestation_aggregator,
state: RwLock::new(genesis_state.clone()),
justified_head,
@ -484,9 +478,6 @@ where
self.block_store.put(&block_root, &ssz_encode(&block)[..])?;
self.state_store.put(&state_root, &ssz_encode(&state)[..])?;
// Update the block DAG.
self.block_graph.add_leaf(&parent_block_root, block_root);
// run the fork_choice add_block logic
self.fork_choice.add_block(&block, &block_root)?;

View File

@ -1,44 +0,0 @@
use parking_lot::{RwLock, RwLockReadGuard};
use std::collections::HashSet;
use types::Hash256;
/// Maintains a view of the block DAG, also known as the "blockchain" (except, it tracks multiple
/// chains eminating from a single root instead of just the head of some canonical chain).
///
/// The BlockGraph does not store the blocks, instead it tracks the block hashes of blocks at the
/// tip of the DAG. It is out of the scope of the object to retrieve blocks.
///
/// Presently, the DAG root (genesis block) is not tracked.
///
/// The BlogGraph is thread-safe due to internal RwLocks.
pub struct BlockGraph {
pub leaves: RwLock<HashSet<Hash256>>,
}
impl BlockGraph {
/// Create a new block graph without any leaves.
pub fn new() -> Self {
Self {
leaves: RwLock::new(HashSet::new()),
}
}
/// Add a new leaf to the block hash graph. Returns `true` if the leaf was built upon another
/// leaf.
pub fn add_leaf(&self, parent: &Hash256, leaf: Hash256) -> bool {
let mut leaves = self.leaves.write();
if leaves.contains(parent) {
leaves.remove(parent);
leaves.insert(leaf);
true
} else {
leaves.insert(leaf);
false
}
}
/// Returns a read-guarded HashSet of all leaf blocks.
pub fn leaves(&self) -> RwLockReadGuard<HashSet<Hash256>> {
self.leaves.read()
}
}

View File

@ -1,169 +0,0 @@
extern crate db;
// TODO: Pull out the dependency on self and beacon_chain
/*
use db::{
stores::{BeaconBlockAtSlotError, BeaconBlockStore},
ClientDB, DBError,
};
use slot_clock::{SlotClock, TestingSlotClockError};
use std::collections::HashSet;
use std::sync::Arc;
use types::{
readers::{BeaconBlockReader, BeaconStateReader},
validator_registry::get_active_validator_indices,
Hash256,
};
#[derive(Debug, PartialEq)]
pub enum Error {
DBError(String),
MissingBeaconState(Hash256),
InvalidBeaconState(Hash256),
MissingBeaconBlock(Hash256),
InvalidBeaconBlock(Hash256),
}
/// A very inefficient implementation of LMD ghost.
pub fn slow_lmd_ghost(&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.get_latest_attestation_target(i as u64) {
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.block_graph.leaves(),
)?;
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)
}
/// Get the total number of votes for some given block root.
///
/// The vote count is incrememented each time an attestation target votes for a block root.
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 == *target {
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

@ -18,10 +18,10 @@
//! [`optimised_lmd_ghost`]: struct.OptimisedLmdGhost.html
//! [`protolambda_lmd_ghost`]: struct.ProtolambdaLmdGhost.html
pub mod basic_lmd_ghost;
pub mod longest_chain;
pub mod optimised_lmd_ghost;
pub mod protolambda_lmd_ghost;
pub mod slow_lmd_ghost;
use db::DBError;
use types::{BeaconBlock, Hash256};
@ -71,7 +71,7 @@ pub enum ForkChoiceAlgorithms {
/// Chooses the longest chain becomes the head. Not for production.
LongestChain,
/// A simple and highly inefficient implementation of LMD ghost.
BasicLMDGhost,
SlowLMDGhost,
/// An optimised version of LMD-GHOST by Vitalik.
OptimmisedLMDGhost,
/// An optimised version of LMD-GHOST by Protolambda.

View File

@ -71,6 +71,42 @@ where
}
}
/// Finds the latest votes weighted by validator balance. Returns a hashmap of block_hash to
/// weighted votes.
pub fn get_latest_votes(
state_root: &Hash256,
block_slot: &Hash256,
) -> Result<HashMap<Has256, u64>, ForkChoiceError> {
// get latest votes
// Note: Votes are weighted by min(balance, MAX_DEPOSIT_AMOUNT) //
// FORK_CHOICE_BALANCE_INCREMENT
// build a hashmap of block_hash to weighted votes
let mut latest_votes: HashMap<Hash256, u64> = HashMap::new();
// gets the current weighted votes
let current_state = self
.state_store
.get_reader(&state_root)?
.ok_or_else(|| ForkChoiceError::MissingBeaconState(state_root))?
.into_beacon_state()
.ok_or_else(|| ForkChoiceError::IncorrectBeaconState(state_root))?;
let active_validator_indices =
get_active_validator_indices(&current_state.validator_registry, block_slot);
for index in active_validator_indices {
let balance =
std::cmp::min(current_state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
/ FORK_CHOICE_BALANCE_INCREMENT;
if balance > 0 {
if let Some(target) = self.latest_attestation_targets.get(&(index as u64)) {
*latest_votes.entry(*target).or_insert_with(|| 0) += balance;
}
}
}
Ok(latest_votes)
}
/// Gets the ancestor at a given height `at_height` of a block specified by `block_hash`.
fn get_ancestor(&mut self, block_hash: Hash256, at_height: u32) -> Option<Hash256> {
// return None if we can't get the block from the db.
@ -271,43 +307,15 @@ impl<T: ClientDB + Sized> ForkChoice for OptimisedLMDGhost<T> {
.block_store
.get_reader(&justified_block_start)?
.ok_or_else(|| ForkChoiceError::MissingBeaconBlock(*justified_block_start))?;
//.into_beacon_block()?;
let block_slot = block.slot();
let block_height = block_slot - GENESIS_SLOT;
let state_root = block.state_root();
// get latest votes
// Note: Votes are weighted by min(balance, MAX_DEPOSIT_AMOUNT) //
// FORK_CHOICE_BALANCE_INCREMENT
// build a hashmap of block_hash to weighted votes
let mut latest_votes: HashMap<Hash256, u64> = HashMap::new();
// gets the current weighted votes
{
let current_state = self
.state_store
.get_reader(&state_root)?
.ok_or_else(|| ForkChoiceError::MissingBeaconState(state_root))?
.into_beacon_state()
.ok_or_else(|| ForkChoiceError::IncorrectBeaconState(state_root))?;
let active_validator_indices =
get_active_validator_indices(&current_state.validator_registry, block_slot);
for index in active_validator_indices {
let balance =
std::cmp::min(current_state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
/ FORK_CHOICE_BALANCE_INCREMENT;
if balance > 0 {
if let Some(target) = self.latest_attestation_targets.get(&(index as u64)) {
*latest_votes.entry(*target).or_insert_with(|| 0) += balance;
}
}
}
}
let mut current_head = *justified_block_start;
let mut latest_votes = self.get_latest_votes(&state_root, &block_slot)?;
// remove any votes that don't relate to our current head.
latest_votes
.retain(|hash, _| self.get_ancestor(*hash, block_height as u32) == Some(current_head));