From 812be825671084037fbeae257635c59b5a95905a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sun, 12 Jan 2020 18:44:04 +1100 Subject: [PATCH] Tidy, add incomplete LmdGhost impl --- eth2/lmd_ghost/src/lib.rs | 1 + eth2/lmd_ghost/src/proto_array.rs | 417 +++++------------------------- 2 files changed, 72 insertions(+), 346 deletions(-) diff --git a/eth2/lmd_ghost/src/lib.rs b/eth2/lmd_ghost/src/lib.rs index e08cbb9df8..3cc74b7484 100644 --- a/eth2/lmd_ghost/src/lib.rs +++ b/eth2/lmd_ghost/src/lib.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use store::Store; use types::{BeaconBlock, EthSpec, Hash256, Slot}; +pub use proto_array::ProtoArrayForkChoice; pub use reduced_tree::ThreadSafeReducedTree; pub type Result = std::result::Result; diff --git a/eth2/lmd_ghost/src/proto_array.rs b/eth2/lmd_ghost/src/proto_array.rs index a12ce67d46..7c10e0c465 100644 --- a/eth2/lmd_ghost/src/proto_array.rs +++ b/eth2/lmd_ghost/src/proto_array.rs @@ -1,10 +1,9 @@ -/// TODO: -/// -/// - Allow for filtering the block tree as per spec (this probably means storing fin/just epochs -/// in the proto_array) +use crate::LmdGhost; use parking_lot::RwLock; use std::collections::HashMap; -use types::{Epoch, Hash256}; +use std::sync::Arc; +use store::Store; +use types::{BeaconBlock, Epoch, EthSpec, Hash256, Slot}; pub const PRUNE_THRESHOLD: usize = 200; @@ -24,14 +23,7 @@ pub enum Error { DeltaOverflow(usize), IndexOverflow(&'static str), RevertedFinalizedEpoch, - StartOutOfBounds, IndexOutOfBounds, - BestChildOutOfBounds { i: usize, len: usize }, - ParentOutOfBounds { i: usize, len: usize }, - BestChildInconsistent, - WeightsInconsistent, - ParentsInconsistent, - BestDescendantInconsistent, } #[derive(Default, PartialEq, Clone)] @@ -41,8 +33,8 @@ pub struct VoteTracker { next_epoch: Epoch, } +#[derive(PartialEq)] pub struct BalanceSnapshot { - state_root: Hash256, balances: Vec, } @@ -52,6 +44,69 @@ pub struct ProtoArrayForkChoice { balances: RwLock, } +impl PartialEq for ProtoArrayForkChoice { + fn eq(&self, other: &Self) -> bool { + *self.proto_array.read() == *other.proto_array.read() + && *self.votes.read() == *other.votes.read() + && *self.balances.read() == *other.balances.read() + } +} + +impl, E: EthSpec> LmdGhost for ProtoArrayForkChoice { + fn new(store: Arc, finalized_block: &BeaconBlock, finalized_root: Hash256) -> Self { + unimplemented!() + } + + fn process_attestation( + &self, + validator_index: usize, + block_hash: Hash256, + block_slot: Slot, + ) -> Result<(), String> { + unimplemented!() + } + + fn process_block(&self, block: &BeaconBlock, block_hash: Hash256) -> Result<(), String> { + unimplemented!() + } + + fn find_head( + &self, + start_block_slot: Slot, + start_block_root: Hash256, + weight: F, + ) -> Result + where + F: Fn(usize) -> Option + Copy, + { + unimplemented!() + } + + fn update_finalized_root( + &self, + finalized_block: &BeaconBlock, + finalized_block_root: Hash256, + ) -> Result<(), String> { + unimplemented!() + } + + fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> { + unimplemented!() + } + + fn verify_integrity(&self) -> Result<(), String> { + unimplemented!() + } + + fn as_bytes(&self) -> Vec { + unimplemented!() + } + + fn from_bytes(bytes: &[u8], store: Arc) -> Result { + unimplemented!() + } +} + impl ProtoArrayForkChoice { pub fn process_attestation(&self, validator_index: usize, block_root: Hash256, epoch: Epoch) { let mut votes = self.votes.write(); @@ -82,7 +137,6 @@ impl ProtoArrayForkChoice { pub fn find_head( &self, - start_block_root: Hash256, justified_epoch: Epoch, justified_root: Hash256, finalized_epoch: Epoch, @@ -165,12 +219,7 @@ pub struct ScoreChange { score_delta: i64, } -#[derive(Clone, Copy)] -pub struct Epochs { - justified: Epoch, - finalized: Epoch, -} - +#[derive(PartialEq)] pub struct ProtoNode { root: Hash256, parent: Option, @@ -191,6 +240,7 @@ impl ProtoNode { } } +#[derive(PartialEq)] pub struct ProtoArray { ffg_update_required: bool, justified_epoch: Epoch, @@ -480,7 +530,7 @@ fn produce_deltas( .ok_or_else(|| Error::NodeUnknown(c.target))?; let v = deltas.get_mut(*i).ok_or_else(|| Error::IndexOutOfBounds)?; - v.saturating_add(c.score_delta); + *v = v.saturating_add(c.score_delta); Ok(()) })?; @@ -488,318 +538,6 @@ fn produce_deltas( Ok(deltas) } -/* -pub struct ProtoArray { - justified_epoch: Epoch, - finalized_epoch: Epoch, - finalized_root: Hash256, - /// Maps the index of some parent node to the index of its best-weighted child. - best_child: Vec>, // TODO: non-zero usize? - /// Maps the index of some node to it's weight. - weights: Vec, - /// Maps the index of a node to the index of its parent. - parents: Vec>, // TODO: non-zero usize with +1 offset? - /// Maps the index of a node to its finalized and justified epochs. - epochs: Vec, - /// Maps the index of a node to the index of its best-weighted descendant. - best_descendant: Vec, // TODO: do I understand this correctly? - // TODO: a `DagNode` stores epochs when we don't need them here. - roots: Vec, - indices: HashMap, -} - -impl ProtoArray { - fn get_parent(&self, i: usize) -> Result, Error> { - self.parents - .get(i) - .copied() - .ok_or_else(|| Error::ParentOutOfBounds { - i, - len: self.parents.len(), - }) - } - - fn get_best_child(&self, i: usize) -> Result, Error> { - self.best_child - .get(i) - .copied() - .ok_or_else(|| Error::BestChildOutOfBounds { - i, - len: self.best_child.len(), - }) - } - - fn get_best_child_mut(&mut self, i: usize) -> Result<&mut Option, Error> { - let len = self.best_child.len(); - self.best_child - .get_mut(i) - .ok_or_else(|| Error::BestChildOutOfBounds { i, len }) - } - - pub fn apply_score_changes(&mut self, changes: Vec) -> Result<(), Error> { - // Check to ensure that the length of all internal arrays is consistent. - self.check_consistency()?; - - let mut d: Vec = vec![0; self.roots.len()]; - - let start = *self - .indices - .get(&self.finalized_root) - .ok_or_else(|| Error::FinalizedNodeUnknown(self.finalized_root))?; - - // Provides safety for later calls in this function. - if start >= d.len() { - return Err(Error::StartOutOfBounds); - } - - changes.iter().try_for_each(|c| { - let i = self - .indices - .get(&c.target) - .ok_or_else(|| Error::NodeUnknown(c.target))?; - let v = d.get_mut(*i).ok_or_else(|| Error::IndexOutOfBounds)?; - - v.saturating_add(c.score_delta); - - Ok(()) - })?; - - // Back-prop diff values - // - // `start` is guaranteed to be greater than or equal to `d.len()` due to a previous check. - for child in (start..d.len()).rev() { - if let Some(parent) = self.get_parent(child)? { - // There is no need to update the weight of the root node because its weight is - // irrelevent. - if parent > 0 { - // TODO: array access safety. - d[parent] += d[child] - } - } - } - - // Apply diffs to weights - for (i, delta) in d.iter().enumerate() { - if *delta > 0 { - // TODO: array access safety - self.weights[i].saturating_add(*delta as u64) - } else { - // TODO: array access safety - self.weights[i].saturating_sub(*delta as u64) - }; - } - - // back-prop best-child/target updates - for i in (start..d.len()).rev() { - // TODO: is this a viable way to build the best descendant? - if let Some(best_child) = self.get_best_child(i)? { - // TODO: array access safety - self.best_descendant[i] = self.best_descendant[best_child] - } - - if d[i] == 0 { - continue; - } - - if let Some(parent) = self.get_parent(i)? { - if let Some(best_child_of_parent) = self.get_best_child(parent)? { - // TODO: does it suffice to compare the deltas? - // TODO: what about tie breaking via hash? - // TODO: array access safety - if best_child_of_parent != i && d[i] >= d[best_child_of_parent] { - // TODO: array access safety - if self.weights[i] > self.weights[best_child_of_parent] { - self.best_child[parent] = Some(i) - } - } - } else { - // TODO: what is this? - // TODO: array access safety - self.best_child[parent] = Some(i) - } - } - } - - Ok(()) - } - - pub fn on_new_node(&mut self, block: DagNode) -> Result<(), Error> { - let i = self.roots.len(); - self.indices.insert(block.root, i); - - // A new node does not have a best child (or any child at all). - self.best_child.push(None); - // A new node has weight 0. - self.weights.push(0); - // TODO: how can a new node not have a parent? Maybe the root node. - if let Some(parent) = block.parent { - if let Some(parent_index) = self.indices.get(&parent).copied() { - self.parents.push(Some(parent_index)); - - // TODO: don't set best child unless the fin/just states match. - // If it is the first child, it is also the best. - let best_child_of_parent = self.get_best_child_mut(parent_index)?; - if best_child_of_parent.is_none() { - *best_child_of_parent = Some(i) - } - } else { - // It is possible that the parent of this block is out-of-bounds (i.e., - // pre-finalizaton). In this case we simply ignore the parent. - self.parents.push(None) - } - } else { - self.parents.push(None) - } - - self.epochs.push(Epochs { - justified: block.justified_epoch, - finalized: block.finalized_epoch, - }); - // The new node points to itself as best-descendant, since it is a leaf. - self.best_descendant.push(i); - self.roots.push(block.root); - - Ok(()) - } - - fn maybe_prune(&mut self) -> Result<(), Error> { - let start = *self - .indices - .get(&self.finalized_root) - .ok_or_else(|| Error::FinalizedNodeUnknown(self.finalized_root))?; - - // Small pruning does not help more than it costs to do. - if start < PRUNE_THRESHOLD { - return Ok(()); - } - - self.best_child = self.best_child.split_off(start); - self.weights = self.weights.split_off(start); - self.parents = self.parents.split_off(start); - self.best_descendant = self.best_descendant.split_off(start); - - for i in 0..start { - // TODO: safe array access. - let key = self.roots[i]; - self.indices.remove(&key); - } - - self.roots = self.roots.split_off(start); - - // Adjust indices back to zero - for (i, root) in self.roots.iter().enumerate() { - // TODO: safe array access. - if let Some(best_child) = self.best_child[i] { - best_child.saturating_sub(start); - } - - // TODO: safe array access. - self.best_descendant[i].saturating_sub(start); - - self.parents[i] = if let Some(parent) = self.parents[i] { - if parent < start { - None - } else { - Some(parent.saturating_sub(start)) - } - } else { - None - }; - - *self - .indices - .get_mut(&root) - .ok_or_else(|| Error::NodeUnknown(*root))? -= start - } - - Ok(()) - } - - pub fn head_fn(&self, justified_root: &Hash256) -> Result { - let mut i = *self - .indices - .get(justified_root) - .ok_or_else(|| Error::JustifiedNodeUnknown(self.finalized_root))?; - - loop { - // TODO: safe array access. - if let Some(best_child) = self.best_child[i] { - i = best_child; - } else { - break; - } - } - - // TODO: safe array access. - Ok(self.roots[i]) - } - - fn update_ffg( - &mut self, - justified_epoch: Epoch, - finalized_epoch: Epoch, - finalized_root: Hash256, - ) -> Result<(), Error> { - if finalized_epoch == self.finalized_epoch && self.finalized_root != finalized_root { - return Err(Error::InvalidFinalizedRootChange); - } - - if finalized_epoch < self.finalized_epoch { - return Err(Error::RevertedFinalizedEpoch); - } - - let finalized_changed = self.finalized_epoch != finalized_epoch; - let justified_changed = self.justified_epoch != justified_epoch; - - self.justified_epoch = justified_epoch; - self.finalized_epoch = finalized_epoch; - self.finalized_root = finalized_root; - - if finalized_changed { - self.maybe_prune()?; - } - - if justified_changed || finalized_changed { - for (i, node_epochs) in self.epochs.iter().copied().enumerate().rev() { - if node_epochs.justified == self.justified_epoch - && node_epochs.finalized == self.finalized_epoch - { - continue; - } - - if let Some(parent) = self.get_parent(i)? { - if let Some(parent_best_child) = self.get_best_child(parent)? { - if parent_best_child == i { - self.best_child[parent] = None - } - } - } - } - } - - Ok(()) - } - - fn check_consistency(&self) -> Result<(), Error> { - let num_nodes = self.roots.len(); - if self.best_child.len() != num_nodes { - return Err(Error::BestChildInconsistent); - } - if self.weights.len() != num_nodes { - return Err(Error::WeightsInconsistent); - } - if self.parents.len() != num_nodes { - return Err(Error::ParentsInconsistent); - } - if self.best_descendant.len() != num_nodes { - return Err(Error::BestDescendantInconsistent); - } - - Ok(()) - } -} -*/ - /// A Vec-wrapper which will grow to match any request. /// /// E.g., a `get` or `insert` to an out-of-bounds element will cause the Vec to grow (using @@ -817,29 +555,16 @@ where } } - pub fn exists(&self, i: usize) -> bool { - i < self.0.len() - } - pub fn get(&mut self, i: usize) -> &T { self.ensure(i); &self.0[i] } - pub fn get_ref(&self, i: usize) -> Option<&T> { - self.0.get(i) - } - pub fn get_mut(&mut self, i: usize) -> &mut T { self.ensure(i); &mut self.0[i] } - pub fn insert(&mut self, i: usize, element: T) { - self.ensure(i); - self.0[i] = element; - } - pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut() }