From 6bb453fc27fdf5f1542656077cc290a9461a9d9b Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 14 Jan 2020 12:15:23 +1100 Subject: [PATCH] Tidy, finish pruning tests --- eth2/lmd_ghost/src/lib.rs | 15 +- eth2/lmd_ghost/src/proto_array.rs | 956 +----------------------------- eth2/lmd_ghost/tests/test.rs | 148 ++++- 3 files changed, 147 insertions(+), 972 deletions(-) diff --git a/eth2/lmd_ghost/src/lib.rs b/eth2/lmd_ghost/src/lib.rs index 6f19c69a81..98b706fdff 100644 --- a/eth2/lmd_ghost/src/lib.rs +++ b/eth2/lmd_ghost/src/lib.rs @@ -156,6 +156,10 @@ impl ProtoArrayForkChoice { let new_balances = justified_state_balances; + proto_array + .maybe_prune(finalized_epoch, finalized_root) + .map_err(|e| format!("find_head maybe_prune failed: {:?}", e))?; + let deltas = compute_deltas( &proto_array.indices, &mut votes, @@ -164,9 +168,6 @@ impl ProtoArrayForkChoice { ) .map_err(|e| format!("find_head compute_deltas failed: {:?}", e))?; - proto_array - .maybe_prune(finalized_epoch, finalized_root) - .map_err(|e| format!("find_head maybe_prune failed: {:?}", e))?; proto_array .apply_score_changes(deltas, justified_epoch) .map_err(|e| format!("find_head apply_score_changes failed: {:?}", e))?; @@ -189,6 +190,14 @@ impl ProtoArrayForkChoice { .map_err(|e| format!("find_head maybe_prune failed: {:?}", e)) } + pub fn set_prune_threshold(&self, prune_threshold: usize) { + self.proto_array.write().prune_threshold = prune_threshold; + } + + pub fn len(&self) -> usize { + self.proto_array.read().nodes.len() + } + fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> { unimplemented!() } diff --git a/eth2/lmd_ghost/src/proto_array.rs b/eth2/lmd_ghost/src/proto_array.rs index 99af80aa7b..bc427516d0 100644 --- a/eth2/lmd_ghost/src/proto_array.rs +++ b/eth2/lmd_ghost/src/proto_array.rs @@ -17,7 +17,7 @@ impl ProtoNode { /// Returns `true` if some node is "better" than the other, according to either weight or root. /// /// If `self == other`, then `true` is returned. - pub fn is_better_then(&self, other: &Self) -> bool { + pub fn is_better_than(&self, other: &Self) -> bool { if self.weight == other.weight { self.root >= other.root } else { @@ -98,7 +98,7 @@ impl ProtoArray { // Apply the delta to the node. if node_delta < 0 { - // Note: I am conflicted about wether to use `saturating_sub` or `checked_sub` + // Note: I am conflicted about whether to use `saturating_sub` or `checked_sub` // here. // // I can't think of any valid reason why `node_delta.abs()` should be greater than @@ -185,7 +185,7 @@ impl ProtoArray { .nodes .get(node_index) .ok_or_else(|| Error::InvalidNodeIndex(node_index))? - .is_better_then(parent_best_child); + .is_better_than(parent_best_child); let current_best_child_is_viable = self.node_is_viable_for_head(parent_best_child); @@ -250,7 +250,7 @@ impl ProtoArray { .get(parent_best_child_index) .ok_or_else(|| Error::InvalidBestChildIndex(parent_best_child_index))?; - if node.is_better_then(parent_best_child) { + if node.is_better_than(parent_best_child) { self.set_best_child(parent_index, node_index)?; } } else { @@ -320,10 +320,7 @@ impl ProtoArray { finalized_epoch: Epoch, finalized_root: Hash256, ) -> Result<(), Error> { - if finalized_epoch == self.finalized_epoch && finalized_root == self.finalized_root { - // Nothing to do. - return Ok(()); - } else if finalized_epoch == self.finalized_epoch && self.finalized_root != finalized_root { + if finalized_epoch == self.finalized_epoch && self.finalized_root != finalized_root { // It's illegal to swap finalized roots on the same epoch (this is reverting a // finalized block). return Err(Error::InvalidFinalizedRootChange); @@ -331,7 +328,7 @@ impl ProtoArray { // It's illegal to swap to an earlier finalized root (this is assumed to be reverting a // finalized block). return Err(Error::RevertedFinalizedEpoch); - } else { + } else if finalized_epoch != self.finalized_epoch { self.finalized_epoch = finalized_epoch; self.finalized_root = finalized_root; self.ffg_update_required = true; @@ -342,8 +339,8 @@ impl ProtoArray { .get(&self.finalized_root) .ok_or_else(|| Error::FinalizedNodeUnknown(self.finalized_root))?; - // Pruning at small numbers incurs more cost than benefit. if finalized_index < self.prune_threshold { + // Pruning at small numbers incurs more cost than benefit. return Ok(()); } @@ -360,9 +357,16 @@ impl ProtoArray { // Drop all the nodes prior to finalization. self.nodes = self.nodes.split_off(finalized_index); - // Iterate through all the existing nodes and adjust their indicies to match the new layout + // Adjust the indices map. + for (_root, index) in self.indices.iter_mut() { + *index = index + .checked_sub(finalized_index) + .ok_or_else(|| Error::IndexOverflow("indices"))?; + } + + // Iterate through all the existing nodes and adjust their indices to match the new layout // of `self.nodes`. - self.nodes.iter_mut().try_for_each(|node| { + for node in self.nodes.iter_mut() { if let Some(parent) = node.parent { // If `node.parent` is less than `finalized_index`, set it to `None`. node.parent = parent.checked_sub(finalized_index); @@ -381,9 +385,7 @@ impl ProtoArray { .ok_or_else(|| Error::IndexOverflow("best_descendant"))?, ); } - - Ok(()) - })?; + } Ok(()) } @@ -422,927 +424,3 @@ impl ProtoArray { node.justified_epoch == self.justified_epoch && node.finalized_epoch == self.finalized_epoch } } - -#[cfg(test)] -mod test_proto_array_fork_choice { - use super::*; - use crate::ProtoArrayForkChoice; - - /// Gives a hash that is not the zero hash (unless i is `usize::max_value)`. - fn get_hash(i: u64) -> Hash256 { - Hash256::from_low_u64_be(i) - } - - /// This tests does not use any validator votes, it just relies on hash-sorting to find the - /// head. - /// - /// The following block graph is built and tested as each block is added (each block has the - /// hash set to the big-endian representation of its number shown here): - /// - /// 0 - /// / \ - /// 2 1 - /// | | - /// 4 3 - /// | - /// 5 <--- justified epoch becomes 1 here, all above are 0. - /// | - /// 6 - #[test] - fn no_votes() { - const VALIDATOR_COUNT: usize = 16; - - let fork_choice = ProtoArrayForkChoice::new(Epoch::new(0), Epoch::new(0), get_hash(0)) - .expect("should create fork choice"); - - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - Hash256::zero(), - "should find genesis block as root when there is only one block" - ); - - // Add block 2 - // - // 0 - // / - // 2 - fork_choice - .process_block(get_hash(2), get_hash(0), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure the head is 2 - // - // 0 - // / - // 2 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(2), - "should find head block with a single chain" - ); - - // Add block 1 - // - // 0 - // / \ - // 2 1 - fork_choice - .process_block(get_hash(1), get_hash(0), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure the head is still 2 - // - // 0 - // / \ - // head-> 2 1 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(2), - "should find the first block, not the second block (it should compare hashes)" - ); - - // Add block 3 - // - // 0 - // / \ - // 2 1 - // | - // 3 - fork_choice - .process_block(get_hash(3), get_hash(1), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure 3 is the head - // - // 0 - // / \ - // 2 1 - // | - // 3 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(2), - "should find the get_hash(2) block" - ); - - // Add block 4 - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - fork_choice - .process_block(get_hash(4), get_hash(2), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure the head is 4. - // - // 0 - // / \ - // 2 1 - // | | - // head-> 4 3 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(4), - "should find the get_hash(4) block" - ); - - // Ensure the head is still 4 whilst the justified epoch is 0. - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - // | - // 5 <- justified epoch = 1 - fork_choice - .process_block(get_hash(5), get_hash(4), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - // Ensure the head is still 4 whilst the justified epoch is 0. - // - // 0 - // / \ - // 2 1 - // | | - // head-> 4 3 - // | - // 5 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(4), - "should find the get_hash(4) block because the get_hash(5) should be filtered out" - ); - - // Ensure there is an error when starting from a block that has the wrong justified epoch. - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - // | - // 5 <- starting from 5 with justified epoch 0 should error. - assert!( - fork_choice - .find_head( - Epoch::new(0), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .is_err(), - "should not allow finding head from a bad justified epoch" - ); - - // Set the justified epoch to 1 and the start block to 5 and ensure 5 is the head. - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - // | - // 5 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(5), - "should find the get_hash(5) block" - ); - - // Add block 6 - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - // | - // 5 - // | - // 6 - fork_choice - .process_block(get_hash(6), get_hash(5), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - // Ensure 6 is the head - // - // 0 - // / \ - // 2 1 - // | | - // 4 3 - // | - // 5 - // | - // 6 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(6), - "should find the get_hash(6) block" - ); - } - - #[test] - fn votes() { - const VALIDATOR_COUNT: usize = 2; - let balances = vec![1; VALIDATOR_COUNT]; - - let fork_choice = ProtoArrayForkChoice::new(Epoch::new(0), Epoch::new(0), get_hash(0)) - .expect("should create fork choice"); - - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - Hash256::zero(), - "should find genesis block as root when there is only one block" - ); - - // Add a block with a hash of 2. - // - // 0 - // / - // 2 - fork_choice - .process_block(get_hash(2), get_hash(0), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure that the head is 2 - // - // 0 - // / - // head-> 2 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(2), - "should find head block with a single chain" - ); - - // Add a block with a hash of 1 that comes off the genesis block (this is a fork compared - // to the previous block). - // - // 0 - // / \ - // 2 1 - fork_choice - .process_block(get_hash(1), get_hash(0), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure that the head is 2 - // - // 0 - // / \ - // head-> 2 1 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(2), - "should find get_hash(2), not get_hash(1) (it should compare hashes)" - ); - - // Add a vote to block 1 - // - // 0 - // / \ - // 2 1 <- +vote - fork_choice - .process_attestation(0, get_hash(1), Epoch::new(1)) - .expect("should process attestation"); - - // Ensure that the head is now 1, beacuse 1 has a vote. - // - // 0 - // / \ - // 2 1 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(1), - "should find the get_hash(1) because it now has a vote" - ); - - // Add a vote to block 2 - // - // 0 - // / \ - // +vote-> 2 1 - fork_choice - .process_attestation(1, get_hash(2), Epoch::new(1)) - .expect("should process attestation"); - - // Ensure that the head is 2 since 1 and 2 both have a vote - // - // 0 - // / \ - // head-> 2 1 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); - - // Add block 3. - // - // 0 - // / \ - // 2 1 - // | - // 3 - fork_choice - .process_block(get_hash(3), get_hash(1), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure that the head is still 2 - // - // 0 - // / \ - // head-> 2 1 - // | - // 3 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); - - // Move validator #0 vote from 1 to 3 - // - // 0 - // / \ - // 2 1 <- -vote - // | - // 3 <- +vote - fork_choice - .process_attestation(0, get_hash(3), Epoch::new(2)) - .expect("should process attestation"); - - // Ensure that the head is still 2 - // - // 0 - // / \ - // head-> 2 1 - // | - // 3 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); - - // Move validator #1 vote from 2 to 1 (this is an equivocation, but fork choice doesn't - // care) - // - // 0 - // / \ - // -vote-> 2 1 <- +vote - // | - // 3 - fork_choice - .process_attestation(1, get_hash(1), Epoch::new(2)) - .expect("should process attestation"); - - // Ensure that the head is now 3 - // - // 0 - // / \ - // 2 1 - // | - // 3 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(3), - "should find get_hash(3)" - ); - - // Add block 4. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - fork_choice - .process_block(get_hash(4), get_hash(3), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Ensure that the head is now 4 - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(4), - "should find get_hash(4)" - ); - - // Add block 5, which has a justified epoch of 1. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / - // 5 <- justified epoch = 1 - fork_choice - .process_block(get_hash(5), get_hash(4), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - // Ensure that 5 is filtered out and the head stays at 4. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 <- head - // / - // 5 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(4), - "should find get_hash(4)" - ); - - // Add block 6, which has a justified epoch of 0. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 <- justified epoch = 0 - fork_choice - .process_block(get_hash(6), get_hash(4), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); - - // Move both votes to 5. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // +2 vote-> 5 6 - fork_choice - .process_attestation(0, get_hash(5), Epoch::new(3)) - .expect("should process attestation"); - fork_choice - .process_attestation(1, get_hash(5), Epoch::new(3)) - .expect("should process attestation"); - - // Add blocks 7, 8 and 9. Adding these blocks helps test the `best_descendant` - // functionality. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / - // 9 - fork_choice - .process_block(get_hash(7), get_hash(5), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - fork_choice - .process_block(get_hash(8), get_hash(7), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - fork_choice - .process_block(get_hash(9), get_hash(8), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - // Ensure that 6 is the head, even though 5 has all the votes. This is testing to ensure - // that 5 is filtered out due to a differing justified epoch. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 <- head - // | - // 7 - // | - // 8 - // / - // 9 - assert_eq!( - fork_choice - .find_head( - Epoch::new(0), - Hash256::zero(), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(6), - "should find get_hash(6)" - ); - - // Change fork-choice justified epoch to 1, and the start block to 5 and ensure that 9 is - // the head. - // - // << Change justified epoch to 1 >> - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / - // head-> 9 - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); - - // Change fork-choice justified epoch to 1, and the start block to 5 and ensure that 9 is - // the head. - // - // << Change justified epoch to 1 >> - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / - // 9 <- +2 votes - fork_choice - .process_attestation(0, get_hash(9), Epoch::new(4)) - .expect("should process attestation"); - fork_choice - .process_attestation(1, get_hash(9), Epoch::new(4)) - .expect("should process attestation"); - - // Add block 10 - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / \ - // 9 10 - fork_choice - .process_block(get_hash(10), get_hash(8), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - // Double-check the head is still 9 (no diagram this time) - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); - - // Introduce 2 more validators into the system - let balances = vec![1; 4]; - - // Have the two new validators vote for 10 - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / \ - // 9 10 <- +2 votes - fork_choice - .process_attestation(2, get_hash(10), Epoch::new(4)) - .expect("should process attestation"); - fork_choice - .process_attestation(3, get_hash(10), Epoch::new(4)) - .expect("should process attestation"); - - // Check the head is now 10. - // - // 0 - // / \ - // 2 1 - // | - // 3 - // | - // 4 - // / \ - // 5 6 - // | - // 7 - // | - // 8 - // / \ - // 9 10 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(10), - "should find get_hash(10)" - ); - - // Set the balances of the last two validators to zero - let balances = vec![1, 1, 0, 0]; - - // Check the head is 9 again. - // - // . - // . - // . - // | - // 8 - // / \ - // head-> 9 10 - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); - - // Set the balances of the last two validators back to 1 - let balances = vec![1; 4]; - - // Check the head is 10. - // - // . - // . - // . - // | - // 8 - // / \ - // 9 10 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(10), - "should find get_hash(10)" - ); - - // Remove the last two validators - let balances = vec![1; 2]; - - // Check the head is 9 again. - // - // . - // . - // . - // | - // 8 - // / \ - // head-> 9 10 - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); - } -} diff --git a/eth2/lmd_ghost/tests/test.rs b/eth2/lmd_ghost/tests/test.rs index d891cc3aec..d99760b4fc 100644 --- a/eth2/lmd_ghost/tests/test.rs +++ b/eth2/lmd_ghost/tests/test.rs @@ -8,19 +8,6 @@ fn get_hash(i: u64) -> Hash256 { /// This tests does not use any validator votes, it just relies on hash-sorting to find the /// head. -/// -/// The following block graph is built and tested as each block is added (each block has the -/// hash set to the big-endian representation of its number shown here): -/// -/// 0 -/// / \ -/// 2 1 -/// | | -/// 4 3 -/// | -/// 5 <--- justified epoch becomes 1 here, all above are 0. -/// | -/// 6 #[test] fn no_votes() { const VALIDATOR_COUNT: usize = 16; @@ -284,6 +271,7 @@ fn no_votes() { ); } +/// This test uses validator votes and tests weight assignment. #[test] fn votes() { const VALIDATOR_COUNT: usize = 2; @@ -564,7 +552,7 @@ fn votes() { // / // 5 <- justified epoch = 1 fork_choice - .process_block(get_hash(5), get_hash(4), Epoch::new(1), Epoch::new(0)) + .process_block(get_hash(5), get_hash(4), Epoch::new(1), Epoch::new(1)) .expect("should process block"); // Ensure that 5 is filtered out and the head stays at 4. @@ -644,13 +632,13 @@ fn votes() { // / // 9 fork_choice - .process_block(get_hash(7), get_hash(5), Epoch::new(1), Epoch::new(0)) + .process_block(get_hash(7), get_hash(5), Epoch::new(1), Epoch::new(1)) .expect("should process block"); fork_choice - .process_block(get_hash(8), get_hash(7), Epoch::new(1), Epoch::new(0)) + .process_block(get_hash(8), get_hash(7), Epoch::new(1), Epoch::new(1)) .expect("should process block"); fork_choice - .process_block(get_hash(9), get_hash(8), Epoch::new(1), Epoch::new(0)) + .process_block(get_hash(9), get_hash(8), Epoch::new(1), Epoch::new(1)) .expect("should process block"); // Ensure that 6 is the head, even though 5 has all the votes. This is testing to ensure @@ -710,8 +698,8 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), @@ -764,7 +752,7 @@ fn votes() { // / \ // 9 10 fork_choice - .process_block(get_hash(10), get_hash(8), Epoch::new(1), Epoch::new(0)) + .process_block(get_hash(10), get_hash(8), Epoch::new(1), Epoch::new(1)) .expect("should process block"); // Double-check the head is still 9 (no diagram this time) @@ -773,8 +761,8 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), @@ -831,8 +819,8 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), @@ -857,8 +845,8 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), @@ -883,8 +871,8 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), @@ -897,6 +885,7 @@ fn votes() { // Check the head is 9 again. // + // (prior blocks ommitted) // . // . // . @@ -909,12 +898,111 @@ fn votes() { .find_head( Epoch::new(1), get_hash(5), - Epoch::new(0), - Hash256::zero(), + Epoch::new(1), + get_hash(5), &balances ) .expect("should find head"), get_hash(9), "should find get_hash(9)" ); + + // Set pruning to an unreachable value. + fork_choice.set_prune_threshold(usize::max_value()); + + // Run find-head to trigger a prune. + assert_eq!( + fork_choice + .find_head( + Epoch::new(1), + get_hash(5), + Epoch::new(1), + get_hash(5), + &balances + ) + .expect("should find head"), + get_hash(9), + "should find get_hash(9)" + ); + + // Ensure that no pruning happened. + assert_eq!(fork_choice.len(), 11, "there should be 11 blocks"); + + // Set pruning to a value that will result in a prune. + fork_choice.set_prune_threshold(1); + + // Run find-head to trigger a prune. + // + // + // 0 + // / \ + // 2 1 + // | + // 3 + // | + // 4 + // -------pruned here ------ + // 5 6 + // | + // 7 + // | + // 8 + // / \ + // head-> 9 10 + assert_eq!( + fork_choice + .find_head( + Epoch::new(1), + get_hash(5), + Epoch::new(1), + get_hash(5), + &balances + ) + .expect("should find head"), + get_hash(9), + "should find get_hash(9)" + ); + + // Ensure that pruning happened. + assert_eq!(fork_choice.len(), 6, "there should be 6 blocks"); + + // Add block 11 + // + // 5 6 + // | + // 7 + // | + // 8 + // / \ + // 9 10 + // | + // 11 + fork_choice + .process_block(get_hash(11), get_hash(9), Epoch::new(1), Epoch::new(1)) + .expect("should process block"); + + // Ensure the head is now 11 + // + // 5 6 + // | + // 7 + // | + // 8 + // / \ + // 9 10 + // | + // head-> 11 + assert_eq!( + fork_choice + .find_head( + Epoch::new(1), + get_hash(5), + Epoch::new(1), + get_hash(5), + &balances + ) + .expect("should find head"), + get_hash(11), + "should find get_hash(11)" + ); }