diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 233ad5f24..3a44fbda5 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -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 { pub block_store: Arc>, pub state_store: Arc>, pub slot_clock: U, - pub block_graph: BlockGraph, pub attestation_aggregator: RwLock, canonical_head: RwLock, finalized_head: RwLock, @@ -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)?; diff --git a/beacon_node/beacon_chain/src/block_graph.rs b/beacon_node/beacon_chain/src/block_graph.rs deleted file mode 100644 index 5af851243..000000000 --- a/beacon_node/beacon_chain/src/block_graph.rs +++ /dev/null @@ -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>, -} - -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> { - self.leaves.read() - } -} diff --git a/eth2/fork_choice/src/basic_lmd_ghost.rs b/eth2/fork_choice/src/basic_lmd_ghost.rs deleted file mode 100644 index b7c3ae89a..000000000 --- a/eth2/fork_choice/src/basic_lmd_ghost.rs +++ /dev/null @@ -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 { - 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( - 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 == *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( - 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/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index 1bf5c0b89..d8e20be54 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -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. diff --git a/eth2/fork_choice/src/optimised_lmd_ghost.rs b/eth2/fork_choice/src/optimised_lmd_ghost.rs index 8c53c305a..69778d606 100644 --- a/eth2/fork_choice/src/optimised_lmd_ghost.rs +++ b/eth2/fork_choice/src/optimised_lmd_ghost.rs @@ -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, 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 = 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(¤t_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 { // return None if we can't get the block from the db. @@ -271,43 +307,15 @@ impl ForkChoice for OptimisedLMDGhost { .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 = 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(¤t_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));