diff --git a/eth2/lmd_ghost/src/proto_array.rs b/eth2/lmd_ghost/src/proto_array.rs index c144d9a496..b6acd6342f 100644 --- a/eth2/lmd_ghost/src/proto_array.rs +++ b/eth2/lmd_ghost/src/proto_array.rs @@ -1058,11 +1058,20 @@ mod test_proto_array_fork_choice { "should find genesis block as root when there is only one block" ); - // Add a block with a hash of 2. + // 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( @@ -1077,12 +1086,20 @@ mod test_proto_array_fork_choice { "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). + // 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( @@ -1097,11 +1114,24 @@ mod test_proto_array_fork_choice { "should find the first block, not the second block (it should compare hashes)" ); - // Add a block with a hash of 3 whos parent is hash of 1. + // 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( @@ -1116,11 +1146,24 @@ mod test_proto_array_fork_choice { "should find the get_hash(2) block" ); - // Add a block with a hash of 4 whos parent is hash of 2. + // 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( @@ -1135,12 +1178,28 @@ mod test_proto_array_fork_choice { "should find the get_hash(4) block" ); - // Add a block with a hash of 5 whos parent is hash of 4, where the justified epoch is - // higher than others. + // 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( @@ -1155,6 +1214,15 @@ mod test_proto_array_fork_choice { "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( @@ -1168,6 +1236,15 @@ mod test_proto_array_fork_choice { "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( @@ -1182,11 +1259,32 @@ mod test_proto_array_fork_choice { "should find the get_hash(5) block" ); - // Add a block with a hash of 6 whos parent is hash of 5. + // 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( @@ -1204,7 +1302,7 @@ mod test_proto_array_fork_choice { #[test] fn votes() { - const VALIDATOR_COUNT: usize = 16; + 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)) @@ -1225,10 +1323,19 @@ mod test_proto_array_fork_choice { ); // 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( @@ -1245,10 +1352,19 @@ mod test_proto_array_fork_choice { // 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( @@ -1263,10 +1379,20 @@ mod test_proto_array_fork_choice { "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( @@ -1281,12 +1407,20 @@ mod test_proto_array_fork_choice { "should find the get_hash(1) because it now has a vote" ); - /* - // Add a block with a hash of 3 whos parent is hash of 1. + // Add a vote to block 2 + // + // 0 + // / \ + // +vote-> 2 1 fork_choice - .process_block(get_hash(3), get_hash(1), Epoch::new(0), Epoch::new(0)) - .expect("should process block"); + .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( @@ -1298,14 +1432,128 @@ mod test_proto_array_fork_choice { ) .expect("should find head"), get_hash(2), - "should find the get_hash(2) block" + "should find get_hash(2)" ); - // Add a block with a hash of 4 whos parent is hash of 2. + // Add block 3. + // + // 0 + // / \ + // 2 1 + // | + // 3 fork_choice - .process_block(get_hash(4), get_hash(2), Epoch::new(0), Epoch::new(0)) + .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( @@ -1317,15 +1565,35 @@ mod test_proto_array_fork_choice { ) .expect("should find head"), get_hash(4), - "should find the get_hash(4) block" + "should find get_hash(4)" ); - // Add a block with a hash of 5 whos parent is hash of 4, where the justified epoch is - // higher than others. + // 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( @@ -1337,22 +1605,82 @@ mod test_proto_array_fork_choice { ) .expect("should find head"), get_hash(4), - "should find the get_hash(4) block because the get_hash(5) should be filtered out" + "should find get_hash(4)" ); - assert!( + // 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"); + + // 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 + assert_eq!( fork_choice .find_head( Epoch::new(0), - get_hash(5), + Hash256::zero(), Epoch::new(0), Hash256::zero(), &balances ) - .is_err(), - "should not allow finding head from a bad justified epoch" + .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 5 is + // the head. + // + // << Change justified epoch to 1 >> + // + // 0 + // / \ + // 2 1 + // | + // 3 + // | + // 4 + // / \ + // head -> 5 6 assert_eq!( fork_choice .find_head( @@ -1364,27 +1692,7 @@ mod test_proto_array_fork_choice { ) .expect("should find head"), get_hash(5), - "should find the get_hash(5) block" + "should find get_hash(5)" ); - - // Add a block with a hash of 6 whos parent is hash of 5. - fork_choice - .process_block(get_hash(6), get_hash(5), Epoch::new(1), Epoch::new(0)) - .expect("should process block"); - - assert_eq!( - fork_choice - .find_head( - Epoch::new(1), - get_hash(5), - Epoch::new(0), - Hash256::zero(), - &balances - ) - .expect("should find head"), - get_hash(6), - "should find the get_hash(6) block" - ); - */ } }