github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/db/kv/finalized_block_roots_test.go (about) 1 package kv 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 9 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 10 "github.com/prysmaticlabs/prysm/proto/interfaces" 11 "github.com/prysmaticlabs/prysm/shared/bytesutil" 12 "github.com/prysmaticlabs/prysm/shared/params" 13 "github.com/prysmaticlabs/prysm/shared/testutil" 14 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 15 "github.com/prysmaticlabs/prysm/shared/testutil/require" 16 ) 17 18 var genesisBlockRoot = bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'}) 19 20 func TestStore_IsFinalizedBlock(t *testing.T) { 21 slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch) 22 db := setupDB(t) 23 ctx := context.Background() 24 25 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot)) 26 27 blks := makeBlocks(t, 0, slotsPerEpoch*3, genesisBlockRoot) 28 require.NoError(t, db.SaveBlocks(ctx, blks)) 29 30 root, err := blks[slotsPerEpoch].Block().HashTreeRoot() 31 require.NoError(t, err) 32 33 cp := ðpb.Checkpoint{ 34 Epoch: 1, 35 Root: root[:], 36 } 37 38 st, err := testutil.NewBeaconState() 39 require.NoError(t, err) 40 // a state is required to save checkpoint 41 require.NoError(t, db.SaveState(ctx, st, root)) 42 require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp)) 43 44 // All blocks up to slotsPerEpoch*2 should be in the finalized index. 45 for i := uint64(0); i < slotsPerEpoch*2; i++ { 46 root, err := blks[i].Block().HashTreeRoot() 47 require.NoError(t, err) 48 assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Block at index %d was not considered finalized in the index", i) 49 } 50 for i := slotsPerEpoch * 3; i < uint64(len(blks)); i++ { 51 root, err := blks[i].Block().HashTreeRoot() 52 require.NoError(t, err) 53 assert.Equal(t, false, db.IsFinalizedBlock(ctx, root), "Block at index %d was considered finalized in the index, but should not have", i) 54 } 55 } 56 57 func TestStore_IsFinalizedBlockGenesis(t *testing.T) { 58 db := setupDB(t) 59 ctx := context.Background() 60 61 blk := testutil.NewBeaconBlock() 62 blk.Block.Slot = 0 63 root, err := blk.Block.HashTreeRoot() 64 require.NoError(t, err) 65 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk))) 66 require.NoError(t, db.SaveGenesisBlockRoot(ctx, root)) 67 assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Finalized genesis block doesn't exist in db") 68 } 69 70 // This test scenario is to test a specific edge case where the finalized block root is not part of 71 // the finalized and canonical chain. 72 // 73 // Example: 74 // 0 1 2 3 4 5 6 slot 75 // a <- b <-- d <- e <- f <- g roots 76 // ^- c 77 // Imagine that epochs are 2 slots and that epoch 1, 2, and 3 are finalized. Checkpoint roots would 78 // be c, e, and g. In this scenario, c was a finalized checkpoint root but no block built upon it so 79 // it should not be considered "final and canonical" in the view at slot 6. 80 func TestStore_IsFinalized_ForkEdgeCase(t *testing.T) { 81 slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch) 82 blocks0 := makeBlocks(t, slotsPerEpoch*0, slotsPerEpoch, genesisBlockRoot) 83 blocks1 := append( 84 makeBlocks(t, slotsPerEpoch*1, 1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1]))), // No block builds off of the first block in epoch. 85 makeBlocks(t, slotsPerEpoch*1+1, slotsPerEpoch-1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1])))..., 86 ) 87 blocks2 := makeBlocks(t, slotsPerEpoch*2, slotsPerEpoch, bytesutil.ToBytes32(sszRootOrDie(t, blocks1[len(blocks1)-1]))) 88 89 db := setupDB(t) 90 ctx := context.Background() 91 92 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot)) 93 require.NoError(t, db.SaveBlocks(ctx, blocks0)) 94 require.NoError(t, db.SaveBlocks(ctx, blocks1)) 95 require.NoError(t, db.SaveBlocks(ctx, blocks2)) 96 97 // First checkpoint 98 checkpoint1 := ðpb.Checkpoint{ 99 Root: sszRootOrDie(t, blocks1[0]), 100 Epoch: 1, 101 } 102 103 st, err := testutil.NewBeaconState() 104 require.NoError(t, err) 105 // A state is required to save checkpoint 106 require.NoError(t, db.SaveState(ctx, st, bytesutil.ToBytes32(checkpoint1.Root))) 107 require.NoError(t, db.SaveFinalizedCheckpoint(ctx, checkpoint1)) 108 // All blocks in blocks0 and blocks1 should be finalized and canonical. 109 for i, block := range append(blocks0, blocks1...) { 110 root := sszRootOrDie(t, block) 111 assert.Equal(t, true, db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)), "%d - Expected block %#x to be finalized", i, root) 112 } 113 114 // Second checkpoint 115 checkpoint2 := ðpb.Checkpoint{ 116 Root: sszRootOrDie(t, blocks2[0]), 117 Epoch: 2, 118 } 119 // A state is required to save checkpoint 120 require.NoError(t, db.SaveState(ctx, st, bytesutil.ToBytes32(checkpoint2.Root))) 121 require.NoError(t, db.SaveFinalizedCheckpoint(ctx, checkpoint2)) 122 // All blocks in blocks0 and blocks2 should be finalized and canonical. 123 for i, block := range append(blocks0, blocks2...) { 124 root := sszRootOrDie(t, block) 125 assert.Equal(t, true, db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)), "%d - Expected block %#x to be finalized", i, root) 126 } 127 // All blocks in blocks1 should be finalized and canonical, except blocks1[0]. 128 for i, block := range blocks1 { 129 root := sszRootOrDie(t, block) 130 if db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)) == (i == 0) { 131 t.Errorf("Expected db.IsFinalizedBlock(ctx, blocks1[%d]) to be %v", i, i != 0) 132 } 133 } 134 } 135 136 func TestStore_IsFinalizedChildBlock(t *testing.T) { 137 slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch) 138 db := setupDB(t) 139 ctx := context.Background() 140 141 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot)) 142 143 blks := makeBlocks(t, 0, slotsPerEpoch*3, genesisBlockRoot) 144 145 require.NoError(t, db.SaveBlocks(ctx, blks)) 146 root, err := blks[slotsPerEpoch].Block().HashTreeRoot() 147 require.NoError(t, err) 148 149 cp := ðpb.Checkpoint{ 150 Epoch: 1, 151 Root: root[:], 152 } 153 154 st, err := testutil.NewBeaconState() 155 require.NoError(t, err) 156 // a state is required to save checkpoint 157 require.NoError(t, db.SaveState(ctx, st, root)) 158 require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp)) 159 160 // All blocks up to slotsPerEpoch should have a finalized child block. 161 for i := uint64(0); i < slotsPerEpoch; i++ { 162 root, err := blks[i].Block().HashTreeRoot() 163 require.NoError(t, err) 164 assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Block at index %d was not considered finalized in the index", i) 165 blk, err := db.FinalizedChildBlock(ctx, root) 166 assert.NoError(t, err) 167 if blk == nil { 168 t.Error("Child block doesn't exist for valid finalized block.") 169 } 170 } 171 } 172 173 func sszRootOrDie(t *testing.T, block interfaces.SignedBeaconBlock) []byte { 174 root, err := block.Block().HashTreeRoot() 175 require.NoError(t, err) 176 return root[:] 177 } 178 179 func makeBlocks(t *testing.T, i, n uint64, previousRoot [32]byte) []interfaces.SignedBeaconBlock { 180 blocks := make([]*ethpb.SignedBeaconBlock, n) 181 ifaceBlocks := make([]interfaces.SignedBeaconBlock, n) 182 for j := i; j < n+i; j++ { 183 parentRoot := make([]byte, 32) 184 copy(parentRoot, previousRoot[:]) 185 blocks[j-i] = testutil.NewBeaconBlock() 186 blocks[j-i].Block.Slot = types.Slot(j + 1) 187 blocks[j-i].Block.ParentRoot = parentRoot 188 var err error 189 previousRoot, err = blocks[j-i].Block.HashTreeRoot() 190 require.NoError(t, err) 191 ifaceBlocks[j-i] = wrapper.WrappedPhase0SignedBeaconBlock(blocks[j-i]) 192 } 193 return ifaceBlocks 194 }