diff --git a/Cargo.lock b/Cargo.lock index b975676dd1..463af8a09c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,11 +212,11 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lighthouse_bootstrap 0.1.0", "lighthouse_metrics 0.1.0", - "lmd_ghost 0.1.0", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "merkle_proof 0.1.0", "operation_pool 0.1.0", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proto_array_fork_choice 0.1.0", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -526,7 +526,6 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "genesis 0.1.0", "lighthouse_bootstrap 0.1.0", - "lmd_ghost 0.1.0", "network 0.1.0", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "prometheus 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2396,17 +2395,6 @@ name = "linked-hash-map" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "lmd_ghost" -version = "0.1.0" -dependencies = [ - "eth2_ssz 0.1.2", - "eth2_ssz_derive 0.1.0", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "types 0.1.0", -] - [[package]] name = "lock_api" version = "0.1.5" @@ -3015,6 +3003,17 @@ dependencies = [ "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proto_array_fork_choice" +version = "0.1.0" +dependencies = [ + "eth2_ssz 0.1.2", + "eth2_ssz_derive 0.1.0", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "types 0.1.0", +] + [[package]] name = "protobuf" version = "2.8.1" @@ -3907,7 +3906,6 @@ dependencies = [ "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lmd_ghost 0.1.0", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "merkle_proof 0.1.0", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index fcf9049c12..7df36537b8 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -33,7 +33,6 @@ eth2_ssz_derive = "0.1.0" state_processing = { path = "../../eth2/state_processing" } tree_hash = "0.1.0" types = { path = "../../eth2/types" } -lmd_ghost = { path = "../../eth2/lmd_ghost" } eth1 = { path = "../eth1" } websocket_server = { path = "../websocket_server" } futures = "0.1.25" @@ -41,6 +40,7 @@ exit-future = "0.1.3" genesis = { path = "../genesis" } integer-sqrt = "0.1" rand = "0.7.2" +proto_array_fork_choice = { path = "../../eth2/proto_array_fork_choice" } [dev-dependencies] tempfile = "3.1.0" diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 90b89c8378..dda557b4d7 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -7,7 +7,6 @@ use crate::fork_choice::{Error as ForkChoiceError, ForkChoice}; use crate::head_tracker::HeadTracker; use crate::metrics; use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY}; -use lmd_ghost::LmdGhost; use operation_pool::{OperationPool, PersistedOperationPool}; use parking_lot::RwLock; use slog::{debug, error, info, trace, warn, Logger}; @@ -109,7 +108,6 @@ pub trait BeaconChainTypes: Send + Sync + 'static { type Store: store::Store; type StoreMigrator: store::Migrate; type SlotClock: slot_clock::SlotClock; - type LmdGhost: LmdGhost; type Eth1Chain: Eth1ChainBackend; type EthSpec: types::EthSpec; type EventHandler: EventHandler; @@ -1033,7 +1031,6 @@ impl BeaconChain { error!( self.log, "Add attestation to fork choice failed"; - "fork_choice_integrity" => format!("{:?}", self.fork_choice.verify_integrity()), "beacon_block_root" => format!("{}", attestation.data.beacon_block_root), "error" => format!("{:?}", e) ); @@ -1366,7 +1363,6 @@ impl BeaconChain { error!( self.log, "Add block to fork choice failed"; - "fork_choice_integrity" => format!("{:?}", self.fork_choice.verify_integrity()), "block_root" => format!("{}", block_root), "error" => format!("{:?}", e), ) diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index da23e53dd4..885965ca53 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -8,9 +8,9 @@ use crate::{ ForkChoice, }; use eth1::Config as Eth1Config; -use lmd_ghost::{LmdGhost, ThreadSafeReducedTree}; use operation_pool::OperationPool; use parking_lot::RwLock; +use proto_array_fork_choice::ProtoArrayForkChoice; use slog::{info, Logger}; use slot_clock::{SlotClock, TestingSlotClock}; use std::marker::PhantomData; @@ -21,42 +21,23 @@ use types::{BeaconBlock, BeaconState, ChainSpec, EthSpec, Hash256, Slot}; /// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing /// functionality and only exists to satisfy the type system. -pub struct Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, ->( +pub struct Witness( PhantomData<( TStore, TStoreMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, TEventHandler, )>, ); -impl - BeaconChainTypes - for Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - > +impl BeaconChainTypes + for Witness where TStore: Store + 'static, TStoreMigrator: store::Migrate + 'static, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -64,7 +45,6 @@ where type Store = TStore; type StoreMigrator = TStoreMigrator; type SlotClock = TSlotClock; - type LmdGhost = TLmdGhost; type Eth1Chain = TEth1Backend; type EthSpec = TEthSpec; type EventHandler = TEventHandler; @@ -96,23 +76,14 @@ pub struct BeaconChainBuilder { log: Option, } -impl +impl BeaconChainBuilder< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, TStoreMigrator: store::Migrate + 'static, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -192,15 +163,7 @@ where let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes()); let p: PersistedBeaconChain< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > = match store.get(&key) { Err(e) => { return Err(format!( @@ -315,15 +278,7 @@ where self, ) -> Result< BeaconChain< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, >, String, > { @@ -393,15 +348,7 @@ where impl BeaconChainBuilder< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - ThreadSafeReducedTree, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, @@ -416,17 +363,9 @@ where /// If this builder is being "resumed" from disk, then rebuild the last fork choice stored to /// the database. Otherwise, create a new, empty fork choice. pub fn reduced_tree_fork_choice(mut self) -> Result { - let store = self - .store - .clone() - .ok_or_else(|| "reduced_tree_fork_choice requires a store")?; - let fork_choice = if let Some(persisted_beacon_chain) = &self.persisted_beacon_chain { - ForkChoice::from_ssz_container( - persisted_beacon_chain.fork_choice.clone(), - store.clone(), - ) - .map_err(|e| format!("Unable to decode fork choice from db: {:?}", e))? + ForkChoice::from_ssz_container(persisted_beacon_chain.fork_choice.clone()) + .map_err(|e| format!("Unable to decode fork choice from db: {:?}", e))? } else { let finalized_checkpoint = &self .finalized_checkpoint @@ -436,13 +375,21 @@ where .genesis_block_root .ok_or_else(|| "fork_choice_backend requires a genesis_block_root")?; - let backend = ThreadSafeReducedTree::new( - store.clone(), - &finalized_checkpoint.beacon_block, + let backend = ProtoArrayForkChoice::new( + // Note: here we set the `justified_epoch` to be the same as the epoch of the + // finalized checkpoint. Whilst this finalized checkpoint may actually point to + // a _later_ justified checkpoint, that checkpoint won't yet exist in the fork + // choice. + finalized_checkpoint.beacon_state.current_epoch(), + finalized_checkpoint.beacon_state.current_epoch(), finalized_checkpoint.beacon_block_root, - ); + )?; - ForkChoice::new(store, backend, genesis_block_root, self.spec.genesis_slot) + ForkChoice::new( + backend, + genesis_block_root, + &finalized_checkpoint.beacon_state, + ) }; self.fork_choice = Some(fork_choice); @@ -451,13 +398,12 @@ where } } -impl +impl BeaconChainBuilder< Witness< TStore, TStoreMigrator, TSlotClock, - TLmdGhost, CachingEth1Backend, TEthSpec, TEventHandler, @@ -467,7 +413,6 @@ where TStore: Store + 'static, TStoreMigrator: store::Migrate + 'static, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, { @@ -503,22 +448,13 @@ where } } -impl +impl BeaconChainBuilder< - Witness< - TStore, - TStoreMigrator, - TestingSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, TStoreMigrator: store::Migrate + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -544,13 +480,12 @@ where } } -impl +impl BeaconChainBuilder< Witness< TStore, TStoreMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, NullEventHandler, @@ -560,7 +495,6 @@ where TStore: Store + 'static, TStoreMigrator: store::Migrate + 'static, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, { diff --git a/beacon_node/beacon_chain/src/fork_choice.rs b/beacon_node/beacon_chain/src/fork_choice.rs index d62f9f8fb3..018412a116 100644 --- a/beacon_node/beacon_chain/src/fork_choice.rs +++ b/beacon_node/beacon_chain/src/fork_choice.rs @@ -1,12 +1,13 @@ use crate::{errors::BeaconChainError, metrics, BeaconChain, BeaconChainTypes}; -use lmd_ghost::LmdGhost; use parking_lot::RwLock; +use proto_array_fork_choice::ProtoArrayForkChoice; use ssz_derive::{Decode, Encode}; -use state_processing::{common::get_attesting_indices, per_slot_processing}; -use std::sync::Arc; -use store::{Error as StoreError, Store}; +use state_processing::common::get_attesting_indices; +use std::marker::PhantomData; +use store::Error as StoreError; use types::{ - Attestation, BeaconBlock, BeaconState, BeaconStateError, Checkpoint, EthSpec, Hash256, Slot, + Attestation, BeaconBlock, BeaconState, BeaconStateError, Checkpoint, Epoch, EthSpec, Hash256, + Slot, }; type Result = std::result::Result; @@ -21,18 +22,142 @@ pub enum Error { BeaconChainError(Box), } +#[derive(PartialEq, Clone, Encode, Decode)] +struct CheckpointBalances { + epoch: Epoch, + root: Hash256, + balances: Vec, +} + +impl Into for CheckpointBalances { + fn into(self) -> Checkpoint { + Checkpoint { + epoch: self.epoch, + root: self.root, + } + } +} + +#[derive(PartialEq, Clone, Encode, Decode)] +struct JustificationManager { + /// The fork choice rule's current view of the justified checkpoint. + justified_checkpoint: CheckpointBalances, + /// The best justified checkpoint we've seen, which may be ahead of `justified_checkpoint`. + best_justified_checkpoint: CheckpointBalances, + /// If `Some`, the justified checkpoint should be updated at this epoch (or later). + update_justified_checkpoint_at: Option, +} + +impl JustificationManager { + pub fn new(genesis_checkpoint: CheckpointBalances) -> Self { + Self { + justified_checkpoint: genesis_checkpoint.clone(), + best_justified_checkpoint: genesis_checkpoint.clone(), + update_justified_checkpoint_at: None, + } + } + + pub fn update(&mut self, chain: &BeaconChain) -> Result<()> { + if self.best_justified_checkpoint.epoch > self.justified_checkpoint.epoch { + let current_slot = chain.slot()?; + let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); + + match self.update_justified_checkpoint_at { + None => { + if Self::compute_slots_since_epoch_start::(current_slot) + < chain.spec.safe_slots_to_update_justified + { + self.justified_checkpoint = self.best_justified_checkpoint.clone(); + } else { + self.update_justified_checkpoint_at = Some(current_epoch + 1) + } + } + Some(epoch) if epoch <= current_epoch => { + self.justified_checkpoint = self.best_justified_checkpoint.clone(); + self.update_justified_checkpoint_at = None + } + _ => {} + } + } + + Ok(()) + } + + /// Checks the given `state` to see if it contains a `current_justified_checkpoint` that is + /// better than `self.best_justified_checkpoint`. If so, the value is updated. + /// + /// Note: this does not update `self.justified_checkpoint`. + pub fn process_state( + &mut self, + state: &BeaconState, + chain: &BeaconChain, + ) -> Result<()> { + let new_checkpoint = &state.current_justified_checkpoint; + + // Only proceeed if the new checkpoint is better than our best checkpoint. + if new_checkpoint.epoch > self.best_justified_checkpoint.epoch { + // From the given state, read the block root at first slot of + // `self.justified_checkpoint.epoch`. If that root matches, then + // `new_justified_checkpoint` is a descendant of `self.justified_checkpoint` and we may + // proceed (see next `if` statement). + let new_checkpoint_ancestor = Self::get_block_root_at_slot( + state, + chain, + new_checkpoint.root, + self.justified_checkpoint + .epoch + .start_slot(T::EthSpec::slots_per_epoch()), + )?; + + if new_checkpoint_ancestor == Some(self.justified_checkpoint.root) { + self.best_justified_checkpoint = CheckpointBalances { + epoch: state.current_justified_checkpoint.epoch, + root: state.current_justified_checkpoint.root, + balances: state.balances.clone().into(), + }; + } + } + + Ok(()) + } + + /// Attempts to get the block root for the given `slot`. + /// + /// First, the `state` is used to see if the slot is within the distance of its historical + /// lists. Then, the `chain` is used which will anchor the search at the given + /// `justified_root`. + fn get_block_root_at_slot( + state: &BeaconState, + chain: &BeaconChain, + justified_root: Hash256, + slot: Slot, + ) -> Result> { + match state.get_block_root(slot) { + Ok(root) => Ok(Some(*root)), + Err(_) => chain + .get_ancestor_block_root(justified_root, slot) + .map_err(Into::into), + } + } + + /// Calculate how far `slot` lies from the start of its epoch. + fn compute_slots_since_epoch_start(slot: Slot) -> u64 { + let slots_per_epoch = T::EthSpec::slots_per_epoch(); + (slot - slot.epoch(slots_per_epoch).start_slot(slots_per_epoch)).as_u64() + } +} + pub struct ForkChoice { - store: Arc, - backend: T::LmdGhost, + backend: ProtoArrayForkChoice, /// Used for resolving the `0x00..00` alias back to genesis. /// /// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root /// whenever the struct was instantiated. genesis_block_root: Hash256, - /// The fork choice rule's current view of the justified checkpoint. - justified_checkpoint: RwLock, - /// The best justified checkpoint we've seen, which may be ahead of `justified_checkpoint`. - best_justified_checkpoint: RwLock, + /// The fork choice rule's current view of the finalized checkpoint. + finalized_checkpoint: RwLock, + justification_manager: RwLock, + _phantom: PhantomData, } impl PartialEq for ForkChoice { @@ -40,8 +165,8 @@ impl PartialEq for ForkChoice { fn eq(&self, other: &Self) -> bool { self.backend == other.backend && self.genesis_block_root == other.genesis_block_root - && *self.justified_checkpoint.read() == *other.justified_checkpoint.read() - && *self.best_justified_checkpoint.read() == *other.best_justified_checkpoint.read() + && *self.justification_manager.read() == *other.justification_manager.read() + && *self.finalized_checkpoint.read() == *other.finalized_checkpoint.read() } } @@ -51,128 +176,49 @@ impl ForkChoice { /// "Genesis" does not necessarily need to be the absolute genesis, it can be some finalized /// block. pub fn new( - store: Arc, - backend: T::LmdGhost, + backend: ProtoArrayForkChoice, genesis_block_root: Hash256, - genesis_slot: Slot, + genesis_state: &BeaconState, ) -> Self { - let justified_checkpoint = Checkpoint { - epoch: genesis_slot.epoch(T::EthSpec::slots_per_epoch()), + let genesis_checkpoint = CheckpointBalances { + epoch: genesis_state.current_epoch(), root: genesis_block_root, + balances: genesis_state.balances.clone().into(), }; + Self { - store: store.clone(), backend, genesis_block_root, - justified_checkpoint: RwLock::new(justified_checkpoint.clone()), - best_justified_checkpoint: RwLock::new(justified_checkpoint), + justification_manager: RwLock::new(JustificationManager::new( + genesis_checkpoint.clone(), + )), + finalized_checkpoint: RwLock::new(genesis_checkpoint.into()), + _phantom: PhantomData, } } - /// Determine whether the fork choice's view of the justified checkpoint should be updated. - /// - /// To prevent the bouncing attack, an update is allowed only in these conditions: - /// - /// * We're in the first SAFE_SLOTS_TO_UPDATE_JUSTIFIED slots of the epoch, or - /// * The new justified checkpoint is a descendant of the current justified checkpoint - fn should_update_justified_checkpoint( - &self, - chain: &BeaconChain, - new_justified_checkpoint: &Checkpoint, - ) -> Result { - if Self::compute_slots_since_epoch_start(chain.slot()?) - < chain.spec.safe_slots_to_update_justified - { - return Ok(true); - } - - let justified_checkpoint = self.justified_checkpoint.read().clone(); - - let current_justified_block = chain - .get_block(&justified_checkpoint.root)? - .ok_or_else(|| Error::MissingBlock(justified_checkpoint.root))?; - - let new_justified_block = chain - .get_block(&new_justified_checkpoint.root)? - .ok_or_else(|| Error::MissingBlock(new_justified_checkpoint.root))?; - - let slots_per_epoch = T::EthSpec::slots_per_epoch(); - - Ok( - new_justified_block.slot > justified_checkpoint.epoch.start_slot(slots_per_epoch) - && chain.get_ancestor_block_root( - new_justified_checkpoint.root, - current_justified_block.slot, - )? == Some(justified_checkpoint.root), - ) - } - - /// Calculate how far `slot` lies from the start of its epoch. - fn compute_slots_since_epoch_start(slot: Slot) -> u64 { - let slots_per_epoch = T::EthSpec::slots_per_epoch(); - (slot - slot.epoch(slots_per_epoch).start_slot(slots_per_epoch)).as_u64() - } - /// Run the fork choice rule to determine the head. pub fn find_head(&self, chain: &BeaconChain) -> Result { let timer = metrics::start_timer(&metrics::FORK_CHOICE_FIND_HEAD_TIMES); - let (start_state, start_block_root, start_block_slot) = { - // Check if we should update our view of the justified checkpoint. - // Doing this check here should be quasi-equivalent to the update in the `on_tick` - // function of the spec, so long as `find_head` is called at least once during the first - // SAFE_SLOTS_TO_UPDATE_JUSTIFIED slots. - let best_justified_checkpoint = self.best_justified_checkpoint.read(); - if self.should_update_justified_checkpoint(chain, &best_justified_checkpoint)? { - *self.justified_checkpoint.write() = best_justified_checkpoint.clone(); - } + self.justification_manager.write().update(chain)?; - let current_justified_checkpoint = self.justified_checkpoint.read().clone(); - - let (block_root, block_justified_slot) = ( - current_justified_checkpoint.root, - current_justified_checkpoint - .epoch - .start_slot(T::EthSpec::slots_per_epoch()), - ); - - let block = chain - .store - .get::>(&block_root)? - .ok_or_else(|| Error::MissingBlock(block_root))?; - - // Resolve the `0x00.. 00` alias back to genesis - let block_root = if block_root == Hash256::zero() { - self.genesis_block_root - } else { - block_root - }; - - let mut state: BeaconState = chain - .store - .get_state(&block.state_root, Some(block.slot))? - .ok_or_else(|| Error::MissingState(block.state_root))?; - - // Fast-forward the state to the start slot of the epoch where it was justified. - for _ in block.slot.as_u64()..block_justified_slot.as_u64() { - per_slot_processing(&mut state, None, &chain.spec) - .map_err(BeaconChainError::SlotProcessingError)? - } - - (state, block_root, block_justified_slot) - }; - - // A function that returns the weight for some validator index. - let weight = |validator_index: usize| -> Option { - start_state - .validators - .get(validator_index) - .map(|v| v.effective_balance) - }; + let justified_checkpoint = self + .justification_manager + .read() + .justified_checkpoint + .clone(); + let finalized_checkpoint = self.finalized_checkpoint.read(); let result = self .backend - .find_head(start_block_slot, start_block_root, weight) + .find_head( + justified_checkpoint.epoch, + justified_checkpoint.root, + finalized_checkpoint.epoch, + finalized_checkpoint.root, + &justified_checkpoint.balances, + ) .map_err(Into::into); metrics::stop_timer(timer); @@ -192,39 +238,32 @@ impl ForkChoice { block_root: Hash256, ) -> Result<()> { let timer = metrics::start_timer(&metrics::FORK_CHOICE_PROCESS_BLOCK_TIMES); - // Note: we never count the block as a latest message, only attestations. - // - // I (Paul H) do not have an explicit reference to this, but I derive it from this - // document: - // - // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md - for attestation in &block.body.attestations { - // If the `data.beacon_block_root` block is not known to us, simply ignore the latest - // vote. - if let Some(block) = self - .store - .get::>(&attestation.data.beacon_block_root)? - { - self.process_attestation(state, attestation, &block)?; - } - } - // Check if we should update our view of the justified checkpoint - if state.current_justified_checkpoint.epoch > self.justified_checkpoint.read().epoch { - *self.best_justified_checkpoint.write() = state.current_justified_checkpoint.clone(); + self.justification_manager + .write() + .process_state(state, chain)?; + self.justification_manager.write().update(chain)?; + + // Note: we never count the block as a latest message, only attestations. + for attestation in &block.body.attestations { + // If the `data.beacon_block_root` block is not known to the fork choice, simply ignore + // the vote. if self - .should_update_justified_checkpoint(chain, &state.current_justified_checkpoint)? + .backend + .contains_block(&attestation.data.beacon_block_root) { - *self.justified_checkpoint.write() = state.current_justified_checkpoint.clone(); + self.process_attestation(state, attestation, block)?; } } // This does not apply a vote to the block, it just makes fork choice aware of the block so // it can still be identified as the head even if it doesn't have any votes. - // - // A case where a block without any votes can be the head is where it is the only child of - // a block that has the majority of votes applied to it. - self.backend.process_block(block, block_root)?; + self.backend.process_block( + block_root, + block.parent_root, + state.current_justified_checkpoint.epoch, + state.finalized_checkpoint.epoch, + )?; metrics::stop_timer(timer); @@ -264,8 +303,11 @@ impl ForkChoice { get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?; for validator_index in validator_indices { - self.backend - .process_attestation(validator_index, block_hash, block.slot)?; + self.backend.process_attestation( + validator_index, + block_hash, + block.slot.epoch(T::EthSpec::slots_per_epoch()), + )?; } } @@ -277,18 +319,10 @@ impl ForkChoice { /// Returns the latest message for a given validator, if any. /// /// Returns `(block_root, block_slot)`. - pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> { + pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> { self.backend.latest_message(validator_index) } - /// Runs an integrity verification function on the underlying fork choice algorithm. - /// - /// Returns `Ok(())` if the underlying fork choice has maintained it's integrity, - /// `Err(description)` otherwise. - pub fn verify_integrity(&self) -> core::result::Result<(), String> { - self.backend.verify_integrity() - } - /// Inform the fork choice that the given block (and corresponding root) have been finalized so /// it may prune it's storage. /// @@ -299,7 +333,10 @@ impl ForkChoice { finalized_block_root: Hash256, ) -> Result<()> { self.backend - .update_finalized_root(finalized_block, finalized_block_root) + .update_finalized_root( + finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()), + finalized_block_root, + ) .map_err(Into::into) } @@ -307,8 +344,8 @@ impl ForkChoice { pub fn as_ssz_container(&self) -> SszForkChoice { SszForkChoice { genesis_block_root: self.genesis_block_root.clone(), - justified_checkpoint: self.justified_checkpoint.read().clone(), - best_justified_checkpoint: self.best_justified_checkpoint.read().clone(), + finalized_checkpoint: self.finalized_checkpoint.read().clone(), + justification_manager: self.justification_manager.read().clone(), backend_bytes: self.backend.as_bytes(), } } @@ -316,15 +353,15 @@ impl ForkChoice { /// Instantiates `Self` from a prior `SszForkChoice`. /// /// The created `Self` will have the same state as the `Self` that created the `SszForkChoice`. - pub fn from_ssz_container(ssz_container: SszForkChoice, store: Arc) -> Result { - let backend = LmdGhost::from_bytes(&ssz_container.backend_bytes, store.clone())?; + pub fn from_ssz_container(ssz_container: SszForkChoice) -> Result { + let backend = ProtoArrayForkChoice::from_bytes(&ssz_container.backend_bytes)?; Ok(Self { - store, backend, genesis_block_root: ssz_container.genesis_block_root, - justified_checkpoint: RwLock::new(ssz_container.justified_checkpoint), - best_justified_checkpoint: RwLock::new(ssz_container.best_justified_checkpoint), + finalized_checkpoint: RwLock::new(ssz_container.finalized_checkpoint), + justification_manager: RwLock::new(ssz_container.justification_manager), + _phantom: PhantomData, }) } } @@ -335,8 +372,8 @@ impl ForkChoice { #[derive(Encode, Decode, Clone)] pub struct SszForkChoice { genesis_block_root: Hash256, - justified_checkpoint: Checkpoint, - best_justified_checkpoint: Checkpoint, + finalized_checkpoint: Checkpoint, + justification_manager: JustificationManager, backend_bytes: Vec, } diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index d9e60fac85..291af59437 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -23,7 +23,6 @@ pub use self::errors::{BeaconChainError, BlockProductionError}; pub use eth1_chain::{Eth1Chain, Eth1ChainBackend}; pub use events::EventHandler; pub use fork_choice::ForkChoice; -pub use lmd_ghost; pub use metrics::scrape_for_metrics; pub use parking_lot; pub use slot_clock; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 4c60fe777c..364843aede 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -5,7 +5,6 @@ use crate::{ AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BlockProcessingOutcome, }; use genesis::interop_genesis_state; -use lmd_ghost::ThreadSafeReducedTree; use rayon::prelude::*; use sloggers::{terminal::TerminalLoggerBuilder, types::Severity, Build}; use slot_clock::TestingSlotClock; @@ -34,7 +33,6 @@ pub type BaseHarnessType = Witness< TStore, TStoreMigrator, TestingSlotClock, - ThreadSafeReducedTree, CachingEth1Backend, TEthSpec, NullEventHandler, diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index e5293cd196..8feed866e4 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -33,7 +33,6 @@ exit-future = "0.1.4" futures = "0.1.29" reqwest = "0.9.22" url = "2.1.0" -lmd_ghost = { path = "../../eth2/lmd_ghost" } eth1 = { path = "../eth1" } genesis = { path = "../genesis" } environment = { path = "../../lighthouse/environment" } diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index 4a219ef0b1..e5f80d2459 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -4,7 +4,6 @@ use crate::Client; use beacon_chain::{ builder::{BeaconChainBuilder, Witness}, eth1_chain::CachingEth1Backend, - lmd_ghost::ThreadSafeReducedTree, slot_clock::{SlotClock, SystemTimeSlotClock}, store::{ migrate::{BackgroundMigrator, Migrate, NullMigrator}, @@ -21,7 +20,6 @@ use genesis::{ generate_deterministic_keypairs, interop_genesis_state, state_from_ssz_file, Eth1GenesisService, }; use lighthouse_bootstrap::Bootstrapper; -use lmd_ghost::LmdGhost; use network::{NetworkConfig, NetworkMessage, Service as NetworkService}; use slog::info; use ssz::Decode; @@ -67,23 +65,14 @@ pub struct ClientBuilder { eth_spec_instance: T::EthSpec, } -impl +impl ClientBuilder< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, TStoreMigrator: store::Migrate, TSlotClock: SlotClock + Clone + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -364,17 +353,8 @@ where /// If type inference errors are being raised, see the comment on the definition of `Self`. pub fn build( self, - ) -> Client< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, - > { + ) -> Client> + { Client { beacon_chain: self.beacon_chain, libp2p_network: self.libp2p_network, @@ -387,15 +367,7 @@ where impl ClientBuilder< - Witness< - TStore, - TStoreMigrator, - TSlotClock, - ThreadSafeReducedTree, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, @@ -432,13 +404,12 @@ where } } -impl +impl ClientBuilder< Witness< TStore, TStoreMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, WebSocketSender, @@ -448,7 +419,6 @@ where TStore: Store + 'static, TStoreMigrator: store::Migrate, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, { @@ -482,13 +452,12 @@ where } } -impl +impl ClientBuilder< Witness< DiskStore, TStoreMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, TEventHandler, @@ -497,7 +466,6 @@ impl, TEthSpec> + 'static, - TLmdGhost: LmdGhost, TEthSpec> + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -532,13 +500,12 @@ where } } -impl +impl ClientBuilder< Witness< SimpleDiskStore, TStoreMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, TEventHandler, @@ -547,7 +514,6 @@ impl, TEthSpec> + 'static, - TLmdGhost: LmdGhost, TEthSpec> + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -561,13 +527,12 @@ where } } -impl +impl ClientBuilder< Witness< MemoryStore, NullMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, TEventHandler, @@ -575,7 +540,6 @@ impl > where TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost, TEthSpec> + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -591,13 +555,12 @@ where } } -impl +impl ClientBuilder< Witness< DiskStore, BackgroundMigrator, TSlotClock, - TLmdGhost, TEth1Backend, TEthSpec, TEventHandler, @@ -605,7 +568,6 @@ impl > where TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost, TEthSpec> + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, @@ -619,13 +581,12 @@ where } } -impl +impl ClientBuilder< Witness< TStore, TStoreMigrator, TSlotClock, - TLmdGhost, CachingEth1Backend, TEthSpec, TEventHandler, @@ -635,7 +596,6 @@ where TStore: Store + 'static, TStoreMigrator: store::Migrate, TSlotClock: SlotClock + 'static, - TLmdGhost: LmdGhost + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, { @@ -721,22 +681,13 @@ where } } -impl +impl ClientBuilder< - Witness< - TStore, - TStoreMigrator, - SystemTimeSlotClock, - TLmdGhost, - TEth1Backend, - TEthSpec, - TEventHandler, - >, + Witness, > where TStore: Store + 'static, TStoreMigrator: store::Migrate, - TLmdGhost: LmdGhost + 'static, TEth1Backend: Eth1ChainBackend + 'static, TEthSpec: EthSpec + 'static, TEventHandler: EventHandler + 'static, diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index eda78383c2..2baef22962 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -11,7 +11,7 @@ pub use eth2_config::Eth2Config; use beacon_chain::{ builder::Witness, eth1_chain::CachingEth1Backend, events::WebSocketSender, - lmd_ghost::ThreadSafeReducedTree, slot_clock::SystemTimeSlotClock, + slot_clock::SystemTimeSlotClock, }; use clap::ArgMatches; use config::get_configs; @@ -28,7 +28,6 @@ pub type ProductionClient = Client< DiskStore, BackgroundMigrator, SystemTimeSlotClock, - ThreadSafeReducedTree, E>, CachingEth1Backend>, E, WebSocketSender, diff --git a/eth2/proto_array_fork_choice/src/lib.rs b/eth2/proto_array_fork_choice/src/lib.rs index 46102c7615..60e4a4da86 100644 --- a/eth2/proto_array_fork_choice/src/lib.rs +++ b/eth2/proto_array_fork_choice/src/lib.rs @@ -201,6 +201,10 @@ impl ProtoArrayForkChoice { self.proto_array.read().nodes.len() } + pub fn contains_block(&self, block_root: &Hash256) -> bool { + self.proto_array.read().indices.contains_key(block_root) + } + pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> { let votes = self.votes.read(); diff --git a/eth2/state_processing/Cargo.toml b/eth2/state_processing/Cargo.toml index 96b8f20145..80b2525ef5 100644 --- a/eth2/state_processing/Cargo.toml +++ b/eth2/state_processing/Cargo.toml @@ -18,7 +18,6 @@ serde_yaml = "0.8.11" eth2_ssz = "0.1.2" beacon_chain = { path = "../../beacon_node/beacon_chain" } store = { path = "../../beacon_node/store" } -lmd_ghost = { path = "../lmd_ghost" } [dependencies]