diff --git a/eth2/lmd_ghost/src/proto_array.rs b/eth2/lmd_ghost/src/proto_array.rs index 6893d3372d..cc5e3fe0d1 100644 --- a/eth2/lmd_ghost/src/proto_array.rs +++ b/eth2/lmd_ghost/src/proto_array.rs @@ -1,24 +1,92 @@ +use parking_lot::RwLock; use std::collections::HashMap; use types::Hash256; pub const PRUNE_THRESHOLD: usize = 200; +#[derive(Default)] +pub struct VoteTracker { + previous: Hash256, + current: Hash256, +} + +pub struct BalanceSnapshot { + state_root: Hash256, + balances: Vec, +} + +pub struct ProtoArrayForkChoice { + proto_array: RwLock, + votes: RwLock>, + balances: RwLock, +} + +impl ProtoArrayForkChoice { + pub fn process_attestation(&self, validator_index: usize, block_root: Hash256) { + self.votes.write().get_mut(validator_index).current = block_root; + } + + pub fn process_block(&self, block_root: Hash256, parent_root: Hash256) -> Result<(), Error> { + let node = DagNode { + block_root, + parent: Some(parent_root), + }; + + self.proto_array.write().on_new_node(node) + } + + fn find_head( + &self, + start_block_root: Hash256, + latest_balances: BalanceSnapshot, + ) -> SuperResult { + let current_balances = self.balances.write(); + let votes = self.votes.read(); + + let mut score_changes = vec![]; + + if current_balances.state_root != latest_balances.state_root { + // Note: here we make an assumption that the size of the validator registry never + // reduces. + for (i, latest) in latest_balances.balances.iter().enumerate() { + if let Some(current) = current_balances.balances.get(i) { + if latest != current { + let vote = votes.get(i); + + if vote != VoteTracker::default() { + score_changes.push(ScoreChange { + target: vote.previous, + score_delta, + }) + } + } + } + } + + // Update the thing + current_balances = latest_balances + } + } + + pub fn update_finalized_root(&self, finalized_root: Hash256) -> Result<(), Error> { + let mut proto_array = self.proto_array.write(); + proto_array.finalized_root = finalized_root; + proto_array.prune() + } +} + pub struct DagNode { block_root: Hash256, parent: Option, } -pub struct Dag { - finalized: Hash256, -} - pub struct ScoreChange { target: Hash256, score_delta: i64, } pub struct ProtoArray { - dag: Dag, + 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. @@ -80,8 +148,8 @@ impl ProtoArray { let start = *self .indices - .get(&self.dag.finalized) - .ok_or_else(|| Error::FinalizedNodeUnknown(self.dag.finalized))?; + .get(&self.finalized_root) + .ok_or_else(|| Error::FinalizedNodeUnknown(self.finalized_root))?; // Provides safety for later calls in this function. if start >= d.len() { @@ -194,8 +262,8 @@ impl ProtoArray { pub fn prune(&mut self) -> Result<(), Error> { let start = *self .indices - .get(&self.dag.finalized) - .ok_or_else(|| Error::FinalizedNodeUnknown(self.dag.finalized))?; + .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 < 200 { @@ -247,8 +315,8 @@ impl ProtoArray { pub fn head_fn(&self) -> Result { let mut i = *self .indices - .get(&self.dag.finalized) - .ok_or_else(|| Error::FinalizedNodeUnknown(self.dag.finalized))?; + .get(&self.finalized_root) + .ok_or_else(|| Error::FinalizedNodeUnknown(self.finalized_root))?; loop { // TODO: safe array access. @@ -281,3 +349,44 @@ impl ProtoArray { 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 +/// Default) to the smallest size required to fulfill the request. +#[derive(Default, Clone, Debug, PartialEq)] +pub struct ElasticList(Vec); + +impl ElasticList +where + T: Default, +{ + fn ensure(&mut self, i: usize) { + if self.0.len() <= i { + self.0.resize_with(i + 1, Default::default); + } + } + + 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; + } +}