diff --git a/eth2/proto_array_fork_choice/tests/test.rs b/eth2/proto_array_fork_choice/tests/test.rs index dac9780b9f..1a86c4819c 100644 --- a/eth2/proto_array_fork_choice/tests/test.rs +++ b/eth2/proto_array_fork_choice/tests/test.rs @@ -1,14 +1,33 @@ use proto_array_fork_choice::ProtoArrayForkChoice; use types::{Epoch, Hash256, Slot}; +#[test] +fn no_votes() { + let tester = ForkChoiceTester::new(Slot::new(0), Epoch::new(1), Epoch::new(1), get_hash(0)); + tester.process_operations(get_no_votes_ops()) +} + +#[test] +fn votes() { + let tester = ForkChoiceTester::new(Slot::new(0), Epoch::new(1), Epoch::new(1), get_hash(0)); + tester.process_operations(get_votes_ops()) +} + +#[derive(Debug, Clone)] pub enum Operation { FindHead { justified_epoch: Epoch, - justified_root: Epoch, + justified_root: Hash256, finalized_epoch: Epoch, justified_state_balances: Vec, expected_head: Hash256, }, + InvalidFindHead { + justified_epoch: Epoch, + justified_root: Hash256, + finalized_epoch: Epoch, + justified_state_balances: Vec, + }, ProcessBlock { slot: Slot, root: Hash256, @@ -21,6 +40,12 @@ pub enum Operation { block_root: Hash256, target_epoch: Epoch, }, + Prune { + finalized_epoch: Epoch, + finalized_root: Hash256, + prune_threshold: usize, + expected_len: usize, + }, } pub struct ForkChoiceTester { @@ -44,6 +69,114 @@ impl ForkChoiceTester { .expect("should create fork choice"), } } + + pub fn process_operations(&self, ops: Vec) { + for (i, op) in ops.into_iter().enumerate() { + self.process_operation(i, op) + } + } + + fn process_operation(&self, op_index: usize, op: Operation) { + match op.clone() { + Operation::FindHead { + justified_epoch, + justified_root, + finalized_epoch, + justified_state_balances, + expected_head, + } => { + let head = self + .fork_choice + .find_head( + justified_epoch, + justified_root, + finalized_epoch, + &justified_state_balances, + ) + .expect(&format!( + "find_head op at index {} returned error", + op_index + )); + + assert_eq!( + head, expected_head, + "Operation at index {} failed checks. Operation: {:?}", + op_index, op + ); + check_bytes_round_trip(&self.fork_choice); + } + Operation::InvalidFindHead { + justified_epoch, + justified_root, + finalized_epoch, + justified_state_balances, + } => { + let result = self.fork_choice.find_head( + justified_epoch, + justified_root, + finalized_epoch, + &justified_state_balances, + ); + + assert!( + result.is_err(), + "Operation at index {} . Operation: {:?}", + op_index, + op + ); + check_bytes_round_trip(&self.fork_choice); + } + Operation::ProcessBlock { + slot, + root, + parent_root, + justified_epoch, + finalized_epoch, + } => { + self.fork_choice + .process_block(slot, root, parent_root, justified_epoch, finalized_epoch) + .expect(&format!( + "process_block op at index {} returned error", + op_index + )); + check_bytes_round_trip(&self.fork_choice); + } + Operation::ProcessAttestation { + validator_index, + block_root, + target_epoch, + } => { + self.fork_choice + .process_attestation(validator_index, block_root, target_epoch) + .expect(&format!( + "process_attestation op at index {} returned error", + op_index + )); + check_bytes_round_trip(&self.fork_choice); + } + Operation::Prune { + finalized_epoch, + finalized_root, + prune_threshold, + expected_len, + } => { + self.fork_choice.set_prune_threshold(prune_threshold); + self.fork_choice + .update_finalized_root(finalized_epoch, finalized_root) + .expect("update_finalized_root op at index {} returned error"); + + // Ensure that no pruning happened. + assert_eq!( + self.fork_choice.len(), + expected_len, + "Prune op at index {} failed with {} instead of {}", + op_index, + self.fork_choice.len(), + expected_len + ); + } + } + } } /// Gives a hash that is not the zero hash (unless i is `usize::max_value)`. @@ -61,348 +194,262 @@ fn check_bytes_round_trip(original: &ProtoArrayForkChoice) { ); } -/// This tests does not use any validator votes, it just relies on hash-sorting to find the -/// head. -#[test] -fn no_votes() { - const VALIDATOR_COUNT: usize = 16; +fn get_no_votes_ops() -> Vec { + let balances = vec![0; 16]; - let fork_choice = - ProtoArrayForkChoice::new(Slot::new(0), Epoch::new(1), Epoch::new(1), get_hash(0)) - .expect("should create fork choice"); - - check_bytes_round_trip(&fork_choice); - - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - Hash256::zero(), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(2), - get_hash(0), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); - - // Ensure the head is 2 - // - // 0 - // / - // 2 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - Hash256::zero(), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(1), - get_hash(0), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); - - // Ensure the head is still 2 - // - // 0 - // / \ - // head-> 2 1 - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - Hash256::zero(), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(3), - get_hash(1), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); - - // Ensure 3 is the head - // - // 0 - // / \ - // 2 1 - // | - // 3 <- head - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - Hash256::zero(), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(4), - get_hash(2), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); - - check_bytes_round_trip(&fork_choice); - - // Ensure the head is 4. - // - // 0 - // / \ - // 2 1 - // | | - // head-> 4 3 - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - Hash256::zero(), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(5), - get_hash(4), - Epoch::new(2), - Epoch::new(1), - ) - .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(1), - Hash256::zero(), - Epoch::new(1), - &[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(1), - get_hash(5), - Epoch::new(1), - &[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(2), - get_hash(5), - Epoch::new(1), - &[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( - Slot::new(0), - get_hash(6), - get_hash(5), - Epoch::new(2), - Epoch::new(1), - ) - .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(2), - get_hash(5), - Epoch::new(1), - &[0; VALIDATOR_COUNT] - ) - .expect("should find head"), - get_hash(6), - "should find the get_hash(6) block" - ); + vec![ + // Check that the head is the finalized block. + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: Hash256::zero(), + }, + // Add block 2 + // + // 0 + // / + // 2 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(2), + parent_root: get_hash(0), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }, + // Ensure the head is 2 + // + // 0 + // / + // 2 <- head + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }, + // Add block 1 + // + // 0 + // / \ + // 2 1 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(1), + parent_root: get_hash(0), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }, + // Ensure the head is still 2 + // + // 0 + // / \ + // head-> 2 1 + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }, + // Add block 3 + // + // 0 + // / \ + // 2 1 + // | + // 3 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(3), + parent_root: get_hash(1), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }, + // Ensure 2 is still the head + // + // 0 + // / \ + // head-> 2 1 + // | + // 3 + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }, + // Add block 4 + // + // 0 + // / \ + // 2 1 + // | | + // 4 3 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(4), + parent_root: get_hash(2), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }, + // Ensure the head is 4. + // + // 0 + // / \ + // 2 1 + // | | + // head-> 4 3 + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(4), + }, + // Add block 5 with a justified epoch of 2 + // + // 0 + // / \ + // 2 1 + // | | + // 4 3 + // | + // 5 <- justified epoch = 2 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(5), + parent_root: get_hash(4), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(1), + }, + // Ensure the head is still 4 whilst the justified epoch is 0. + // + // 0 + // / \ + // 2 1 + // | | + // head-> 4 3 + // | + // 5 + Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: Hash256::zero(), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(4), + }, + // 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. + Operation::InvalidFindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + }, + // Set the justified epoch to 2 and the start block to 5 and ensure 5 is the head. + // + // 0 + // / \ + // 2 1 + // | | + // 4 3 + // | + // 5 <- head + Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(5), + }, + // Add block 6 + // + // 0 + // / \ + // 2 1 + // | | + // 4 3 + // | + // 5 + // | + // 6 + Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(6), + parent_root: get_hash(5), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(1), + }, + // Ensure 6 is the head + // + // 0 + // / \ + // 2 1 + // | | + // 4 3 + // | + // 5 + // | + // 6 <- head + Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(6), + }, + ] } -/// This test uses validator votes and tests weight assignment. -#[test] -fn votes() { - const VALIDATOR_COUNT: usize = 2; - let balances = vec![1; VALIDATOR_COUNT]; +fn get_votes_ops() -> Vec { + let mut balances = vec![1; 2]; + let mut ops = vec![]; - let fork_choice = - ProtoArrayForkChoice::new(Slot::new(0), Epoch::new(1), Epoch::new(1), get_hash(0)) - .expect("should create fork choice"); - - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - Hash256::zero(), - "should find genesis block as root when there is only one block" - ); + // Ensure that the head starts at the finalized block. + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(0), + }); // Add a block with a hash of 2. // // 0 // / // 2 - fork_choice - .process_block( - Slot::new(0), - get_hash(2), - get_hash(0), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(2), + parent_root: get_hash(0), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }); // Ensure that the head is 2 // // 0 // / // head-> 2 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(2), - "should find head block with a single chain" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }); // Add a block with a hash of 1 that comes off the genesis block (this is a fork compared // to the previous block). @@ -410,72 +457,74 @@ fn votes() { // 0 // / \ // 2 1 - fork_choice - .process_block( - Slot::new(0), - get_hash(1), - get_hash(0), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(1), + parent_root: get_hash(0), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }); - // Ensure that the head is 2 + // Ensure that the head is still 2 // // 0 // / \ // head-> 2 1 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(2), - "should find get_hash(2), not get_hash(1) (it should compare hashes)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }); // Add a vote to block 1 // // 0 // / \ // 2 1 <- +vote - fork_choice - .process_attestation(0, get_hash(1), Epoch::new(2)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 0, + block_root: get_hash(1), + target_epoch: Epoch::new(2), + }); // Ensure that the head is now 1, beacuse 1 has a vote. // // 0 // / \ // 2 1 <- head - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(1), - "should find the get_hash(1) because it now has a vote" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(1), + }); // Add a vote to block 2 // // 0 // / \ // +vote-> 2 1 - fork_choice - .process_attestation(1, get_hash(2), Epoch::new(2)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 1, + block_root: get_hash(2), + target_epoch: Epoch::new(2), + }); // 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(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }); // Add block 3. // @@ -484,15 +533,13 @@ fn votes() { // 2 1 // | // 3 - fork_choice - .process_block( - Slot::new(0), - get_hash(3), - get_hash(1), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(3), + parent_root: get_hash(1), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }); // Ensure that the head is still 2 // @@ -501,13 +548,13 @@ fn votes() { // head-> 2 1 // | // 3 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }); // Move validator #0 vote from 1 to 3 // @@ -516,9 +563,11 @@ fn votes() { // 2 1 <- -vote // | // 3 <- +vote - fork_choice - .process_attestation(0, get_hash(3), Epoch::new(3)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 0, + block_root: get_hash(3), + target_epoch: Epoch::new(3), + }); // Ensure that the head is still 2 // @@ -527,13 +576,13 @@ fn votes() { // head-> 2 1 // | // 3 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(2), - "should find get_hash(2)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(2), + }); // Move validator #1 vote from 2 to 1 (this is an equivocation, but fork choice doesn't // care) @@ -543,9 +592,11 @@ fn votes() { // -vote-> 2 1 <- +vote // | // 3 - fork_choice - .process_attestation(1, get_hash(1), Epoch::new(3)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 1, + block_root: get_hash(1), + target_epoch: Epoch::new(3), + }); // Ensure that the head is now 3 // @@ -554,13 +605,13 @@ fn votes() { // 2 1 // | // 3 <- head - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(3), - "should find get_hash(3)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(3), + }); // Add block 4. // @@ -571,15 +622,13 @@ fn votes() { // 3 // | // 4 - fork_choice - .process_block( - Slot::new(0), - get_hash(4), - get_hash(3), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(4), + parent_root: get_hash(3), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }); // Ensure that the head is now 4 // @@ -590,15 +639,15 @@ fn votes() { // 3 // | // 4 <- head - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(4), - "should find get_hash(4)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(4), + }); - // Add block 5, which has a justified epoch of 1. + // Add block 5, which has a justified epoch of 2. // // 0 // / \ @@ -608,18 +657,14 @@ fn votes() { // | // 4 // / - // 5 <- justified epoch = 1 - fork_choice - .process_block( - Slot::new(0), - get_hash(5), - get_hash(4), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); - - check_bytes_round_trip(&fork_choice); + // 5 <- justified epoch = 2 + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(5), + parent_root: get_hash(4), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); // Ensure that 5 is filtered out and the head stays at 4. // @@ -632,13 +677,13 @@ fn votes() { // 4 <- head // / // 5 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(4), - "should find get_hash(4)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(4), + }); // Add block 6, which has a justified epoch of 0. // @@ -651,15 +696,13 @@ fn votes() { // 4 // / \ // 5 6 <- justified epoch = 0 - fork_choice - .process_block( - Slot::new(0), - get_hash(6), - get_hash(4), - Epoch::new(1), - Epoch::new(1), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(6), + parent_root: get_hash(4), + justified_epoch: Epoch::new(1), + finalized_epoch: Epoch::new(1), + }); // Move both votes to 5. // @@ -672,12 +715,16 @@ fn votes() { // 4 // / \ // +2 vote-> 5 6 - fork_choice - .process_attestation(0, get_hash(5), Epoch::new(4)) - .expect("should process attestation"); - fork_choice - .process_attestation(1, get_hash(5), Epoch::new(4)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 0, + block_root: get_hash(5), + target_epoch: Epoch::new(4), + }); + ops.push(Operation::ProcessAttestation { + validator_index: 1, + block_root: get_hash(5), + target_epoch: Epoch::new(4), + }); // Add blocks 7, 8 and 9. Adding these blocks helps test the `best_descendant` // functionality. @@ -697,33 +744,27 @@ fn votes() { // 8 // / // 9 - fork_choice - .process_block( - Slot::new(0), - get_hash(7), - get_hash(5), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); - fork_choice - .process_block( - Slot::new(0), - get_hash(8), - get_hash(7), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); - fork_choice - .process_block( - Slot::new(0), - get_hash(9), - get_hash(8), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(7), + parent_root: get_hash(5), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(8), + parent_root: get_hash(7), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(9), + parent_root: get_hash(8), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); // 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. @@ -743,13 +784,13 @@ fn votes() { // 8 // / // 9 - assert_eq!( - fork_choice - .find_head(Epoch::new(1), Hash256::zero(), Epoch::new(1), &balances) - .expect("should find head"), - get_hash(6), - "should find get_hash(6)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(1), + justified_root: get_hash(0), + finalized_epoch: Epoch::new(1), + justified_state_balances: balances.clone(), + expected_head: get_hash(6), + }); // Change fork-choice justified epoch to 1, and the start block to 5 and ensure that 9 is // the head. @@ -771,13 +812,13 @@ fn votes() { // 8 // / // head-> 9 - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); // Change fork-choice justified epoch to 1, and the start block to 5 and ensure that 9 is // the head. @@ -799,12 +840,16 @@ fn votes() { // 8 // / // 9 <- +2 votes - fork_choice - .process_attestation(0, get_hash(9), Epoch::new(5)) - .expect("should process attestation"); - fork_choice - .process_attestation(1, get_hash(9), Epoch::new(5)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 0, + block_root: get_hash(9), + target_epoch: Epoch::new(5), + }); + ops.push(Operation::ProcessAttestation { + validator_index: 1, + block_root: get_hash(9), + target_epoch: Epoch::new(5), + }); // Add block 10 // @@ -823,27 +868,25 @@ fn votes() { // 8 // / \ // 9 10 - fork_choice - .process_block( - Slot::new(0), - get_hash(10), - get_hash(8), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(10), + parent_root: get_hash(8), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); // Double-check the head is still 9 (no diagram this time) - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); // Introduce 2 more validators into the system - let balances = vec![1; 4]; + balances = vec![1; 4]; // Have the two new validators vote for 10 // @@ -862,12 +905,16 @@ fn votes() { // 8 // / \ // 9 10 <- +2 votes - fork_choice - .process_attestation(2, get_hash(10), Epoch::new(5)) - .expect("should process attestation"); - fork_choice - .process_attestation(3, get_hash(10), Epoch::new(5)) - .expect("should process attestation"); + ops.push(Operation::ProcessAttestation { + validator_index: 2, + block_root: get_hash(10), + target_epoch: Epoch::new(5), + }); + ops.push(Operation::ProcessAttestation { + validator_index: 3, + block_root: get_hash(10), + target_epoch: Epoch::new(5), + }); // Check the head is now 10. // @@ -886,16 +933,16 @@ fn votes() { // 8 // / \ // 9 10 <- head - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(10), - "should find get_hash(10)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(10), + }); // Set the balances of the last two validators to zero - let balances = vec![1, 1, 0, 0]; + balances = vec![1, 1, 0, 0]; // Check the head is 9 again. // @@ -906,16 +953,16 @@ fn votes() { // 8 // / \ // head-> 9 10 - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); // Set the balances of the last two validators back to 1 - let balances = vec![1; 4]; + balances = vec![1; 4]; // Check the head is 10. // @@ -926,20 +973,20 @@ fn votes() { // 8 // / \ // 9 10 <- head - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(10), - "should find get_hash(10)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(10), + }); // Remove the last two validators - let balances = vec![1; 2]; + balances = vec![1; 2]; // Check the head is 9 again. // - // (prior blocks ommitted) + // (prior blocks omitted for brevity) // . // . // . @@ -947,41 +994,32 @@ fn votes() { // 8 // / \ // head-> 9 10 - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); - check_bytes_round_trip(&fork_choice); + // Ensure that pruning below the prune threshold does not prune. + ops.push(Operation::Prune { + finalized_epoch: Epoch::new(2), + finalized_root: get_hash(5), + prune_threshold: usize::max_value(), + expected_len: 11, + }); - // Set pruning to an unreachable value. - fork_choice.set_prune_threshold(usize::max_value()); - fork_choice - .update_finalized_root(Epoch::new(2), get_hash(5)) - .expect("should update finalized root"); + // Run find-head, ensure the no-op prune didn't change the head. + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); - // Ensure that no pruning happened. - assert_eq!(fork_choice.len(), 11, "there should be 11 blocks"); - - // Run find-head - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); - - // Set pruning to a value that will result in a prune. - fork_choice.set_prune_threshold(1); - fork_choice - .update_finalized_root(Epoch::new(2), get_hash(5)) - .expect("should update finalized root"); - - // Run find-head + // Ensure that pruning above the prune threshold does prune. // // // 0 @@ -998,19 +1036,22 @@ fn votes() { // | // 8 // / \ - // head-> 9 10 - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(9), - "should find get_hash(9)" - ); + // 9 10 + ops.push(Operation::Prune { + finalized_epoch: Epoch::new(2), + finalized_root: get_hash(5), + prune_threshold: 1, + expected_len: 6, + }); - // Ensure that pruning happened. - assert_eq!(fork_choice.len(), 6, "there should be 6 blocks"); - - check_bytes_round_trip(&fork_choice); + // Run find-head, ensure the prune didn't change the head. + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(9), + }); // Add block 11 // @@ -1023,15 +1064,13 @@ fn votes() { // 9 10 // | // 11 - fork_choice - .process_block( - Slot::new(0), - get_hash(11), - get_hash(9), - Epoch::new(2), - Epoch::new(2), - ) - .expect("should process block"); + ops.push(Operation::ProcessBlock { + slot: Slot::new(0), + root: get_hash(11), + parent_root: get_hash(9), + justified_epoch: Epoch::new(2), + finalized_epoch: Epoch::new(2), + }); // Ensure the head is now 11 // @@ -1044,11 +1083,13 @@ fn votes() { // 9 10 // | // head-> 11 - assert_eq!( - fork_choice - .find_head(Epoch::new(2), get_hash(5), Epoch::new(2), &balances) - .expect("should find head"), - get_hash(11), - "should find get_hash(11)" - ); + ops.push(Operation::FindHead { + justified_epoch: Epoch::new(2), + justified_root: get_hash(5), + finalized_epoch: Epoch::new(2), + justified_state_balances: balances.clone(), + expected_head: get_hash(11), + }); + + ops }