github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/forkchoice/protoarray/vote_test.go (about)

     1  package protoarray
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/prysmaticlabs/prysm/shared/params"
     8  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
     9  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    10  )
    11  
    12  func TestVotes_CanFindHead(t *testing.T) {
    13  	balances := []uint64{1, 1}
    14  	f := setup(1, 1)
    15  
    16  	// The head should always start at the finalized block.
    17  	r, err := f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    18  	require.NoError(t, err)
    19  	assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis")
    20  
    21  	// Insert block 2 into the tree and verify head is at 2:
    22  	//         0
    23  	//        /
    24  	//       2 <- head
    25  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
    26  
    27  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    28  	require.NoError(t, err)
    29  	assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
    30  
    31  	// Insert block 1 into the tree and verify head is still at 2:
    32  	//            0
    33  	//           / \
    34  	//  head -> 2  1
    35  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
    36  
    37  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    38  	require.NoError(t, err)
    39  	assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
    40  
    41  	// Add a vote to block 1 of the tree and verify head is switched to 1:
    42  	//            0
    43  	//           / \
    44  	//          2  1 <- +vote, new head
    45  	f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(1), 2)
    46  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    47  	require.NoError(t, err)
    48  	assert.Equal(t, indexToHash(1), r, "Incorrect head for with justified epoch at 1")
    49  
    50  	// Add a vote to block 2 of the tree and verify head is switched to 2:
    51  	//                     0
    52  	//                    / \
    53  	// vote, new head -> 2  1
    54  	f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(2), 2)
    55  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    56  	require.NoError(t, err)
    57  	assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
    58  
    59  	// Insert block 3 into the tree and verify head is still at 2:
    60  	//            0
    61  	//           / \
    62  	//  head -> 2  1
    63  	//             |
    64  	//             3
    65  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(3), indexToHash(1), [32]byte{}, 1, 1))
    66  
    67  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    68  	require.NoError(t, err)
    69  	assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
    70  
    71  	// Move validator 0's vote from 1 to 3 and verify head is still at 2:
    72  	//            0
    73  	//           / \
    74  	//  head -> 2  1 <- old vote
    75  	//             |
    76  	//             3 <- new vote
    77  	f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(3), 3)
    78  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    79  	require.NoError(t, err)
    80  	assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
    81  
    82  	// Move validator 1's vote from 2 to 1 and verify head is switched to 3:
    83  	//               0
    84  	//              / \
    85  	// old vote -> 2  1 <- new vote
    86  	//                |
    87  	//                3 <- head
    88  	f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(1), 3)
    89  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
    90  	require.NoError(t, err)
    91  	assert.Equal(t, indexToHash(3), r, "Incorrect head for with justified epoch at 1")
    92  
    93  	// Insert block 4 into the tree and verify head is at 4:
    94  	//            0
    95  	//           / \
    96  	//          2  1
    97  	//             |
    98  	//             3
    99  	//             |
   100  	//             4 <- head
   101  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(4), indexToHash(3), [32]byte{}, 1, 1))
   102  
   103  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
   104  	require.NoError(t, err)
   105  	assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
   106  
   107  	// Insert block 5 with justified epoch 2, it should be filtered out:
   108  	//            0
   109  	//           / \
   110  	//          2  1
   111  	//             |
   112  	//             3
   113  	//             |
   114  	//             4 <- head
   115  	//            /
   116  	//           5 <- justified epoch = 2
   117  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(5), indexToHash(4), [32]byte{}, 2, 2))
   118  
   119  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
   120  	require.NoError(t, err)
   121  	assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
   122  
   123  	// Insert block 6 with justified epoch 0:
   124  	//            0
   125  	//           / \
   126  	//          2  1
   127  	//             |
   128  	//             3
   129  	//             |
   130  	//             4 <- head
   131  	//            / \
   132  	//           5  6 <- justified epoch = 0
   133  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), [32]byte{}, 1, 1))
   134  
   135  	// Moved 2 votes to block 5:
   136  	//            0
   137  	//           / \
   138  	//          2  1
   139  	//             |
   140  	//             3
   141  	//             |
   142  	//             4
   143  	//            / \
   144  	// 2 votes-> 5  6
   145  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), [32]byte{}, 1, 1))
   146  
   147  	f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
   148  
   149  	// Inset blocks 7, 8 and 9:
   150  	// 6 should still be the head, even though 5 has all the votes.
   151  	//            0
   152  	//           / \
   153  	//          2  1
   154  	//             |
   155  	//             3
   156  	//             |
   157  	//             4
   158  	//            / \
   159  	//           5  6 <- head
   160  	//           |
   161  	//           7
   162  	//           |
   163  	//           8
   164  	//           |
   165  	//           9
   166  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(7), indexToHash(5), [32]byte{}, 2, 2))
   167  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(8), indexToHash(7), [32]byte{}, 2, 2))
   168  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(9), indexToHash(8), [32]byte{}, 2, 2))
   169  
   170  	r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
   171  	require.NoError(t, err)
   172  	assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 1")
   173  
   174  	// Update fork choice justified epoch to 1 and start block to 5.
   175  	// Verify 9 is the head:
   176  	//            0
   177  	//           / \
   178  	//          2  1
   179  	//             |
   180  	//             3
   181  	//             |
   182  	//             4
   183  	//            / \
   184  	//           5  6
   185  	//           |
   186  	//           7
   187  	//           |
   188  	//           8
   189  	//           |
   190  	//           9 <- head
   191  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   192  	require.NoError(t, err)
   193  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2")
   194  
   195  	// Insert block 10 and 2 validators updated their vote to 9.
   196  	// Verify 9 is the head:
   197  	//             0
   198  	//            / \
   199  	//           2  1
   200  	//              |
   201  	//              3
   202  	//              |
   203  	//              4
   204  	//             / \
   205  	//            5  6
   206  	//            |
   207  	//            7
   208  	//            |
   209  	//            8
   210  	//           / \
   211  	// 2 votes->9  10
   212  	f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
   213  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(10), indexToHash(8), [32]byte{}, 2, 2))
   214  
   215  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   216  	require.NoError(t, err)
   217  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2")
   218  
   219  	// Add 3 more validators to the system.
   220  	balances = []uint64{1, 1, 1, 1, 1}
   221  	// The new validators voted for 10.
   222  	f.ProcessAttestation(context.Background(), []uint64{2, 3, 4}, indexToHash(10), 5)
   223  	// The new head should be 10.
   224  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   225  	require.NoError(t, err)
   226  	assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2")
   227  
   228  	// Set the balances of the last 2 validators to 0.
   229  	balances = []uint64{1, 1, 1, 0, 0}
   230  	// The head should be back to 9.
   231  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   232  	require.NoError(t, err)
   233  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1")
   234  
   235  	// Set the balances back to normal.
   236  	balances = []uint64{1, 1, 1, 1, 1}
   237  	// The head should be back to 10.
   238  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   239  	require.NoError(t, err)
   240  	assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2")
   241  
   242  	// Remove the last 2 validators.
   243  	balances = []uint64{1, 1, 1}
   244  	// The head should be back to 9.
   245  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   246  	require.NoError(t, err)
   247  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1")
   248  
   249  	// Verify pruning below the prune threshold does not affect head.
   250  	f.store.pruneThreshold = 1000
   251  	require.NoError(t, f.store.prune(context.Background(), indexToHash(5)))
   252  	assert.Equal(t, 11, len(f.store.nodes), "Incorrect nodes length after prune")
   253  
   254  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   255  	require.NoError(t, err)
   256  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2")
   257  
   258  	// Verify pruning above the prune threshold does prune:
   259  	//          0
   260  	//         / \
   261  	//        2   1
   262  	//            |
   263  	//            3
   264  	//            |
   265  	//            4
   266  	// -------pruned here ------
   267  	//          5   6
   268  	//          |
   269  	//          7
   270  	//          |
   271  	//          8
   272  	//         / \
   273  	//        9  10
   274  	f.store.pruneThreshold = 1
   275  	require.NoError(t, f.store.prune(context.Background(), indexToHash(5)))
   276  	assert.Equal(t, 6, len(f.store.nodes), "Incorrect nodes length after prune")
   277  
   278  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   279  	require.NoError(t, err)
   280  	assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2")
   281  
   282  	// Insert new block 11 and verify head is at 11.
   283  	//          5   6
   284  	//          |
   285  	//          7
   286  	//          |
   287  	//          8
   288  	//         / \
   289  	//        9  10
   290  	//        |
   291  	// head-> 11
   292  	require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(11), indexToHash(9), [32]byte{}, 2, 2))
   293  
   294  	r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
   295  	require.NoError(t, err)
   296  	assert.Equal(t, indexToHash(11), r, "Incorrect head for with justified epoch at 2")
   297  }