github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/db/kv/state_test.go (about) 1 package kv 2 3 import ( 4 "context" 5 "reflect" 6 "testing" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 12 "github.com/prysmaticlabs/prysm/proto/interfaces" 13 "github.com/prysmaticlabs/prysm/shared/bytesutil" 14 "github.com/prysmaticlabs/prysm/shared/params" 15 "github.com/prysmaticlabs/prysm/shared/testutil" 16 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 17 "github.com/prysmaticlabs/prysm/shared/testutil/require" 18 "gopkg.in/d4l3k/messagediff.v1" 19 ) 20 21 func TestState_CanSaveRetrieve(t *testing.T) { 22 db := setupDB(t) 23 24 r := [32]byte{'A'} 25 26 require.Equal(t, false, db.HasState(context.Background(), r)) 27 28 st, err := testutil.NewBeaconState() 29 require.NoError(t, err) 30 require.NoError(t, st.SetSlot(100)) 31 32 require.NoError(t, db.SaveState(context.Background(), st, r)) 33 assert.Equal(t, true, db.HasState(context.Background(), r)) 34 35 savedS, err := db.State(context.Background(), r) 36 require.NoError(t, err) 37 38 if !reflect.DeepEqual(st.InnerStateUnsafe(), savedS.InnerStateUnsafe()) { 39 diff, _ := messagediff.PrettyDiff(st.InnerStateUnsafe(), savedS.InnerStateUnsafe()) 40 t.Errorf("Did not retrieve saved state: %v", diff) 41 } 42 43 savedS, err = db.State(context.Background(), [32]byte{'B'}) 44 require.NoError(t, err) 45 assert.Equal(t, iface.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil") 46 } 47 48 func TestGenesisState_CanSaveRetrieve(t *testing.T) { 49 db := setupDB(t) 50 51 headRoot := [32]byte{'B'} 52 53 st, err := testutil.NewBeaconState() 54 require.NoError(t, err) 55 require.NoError(t, st.SetSlot(1)) 56 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), headRoot)) 57 require.NoError(t, db.SaveState(context.Background(), st, headRoot)) 58 59 savedGenesisS, err := db.GenesisState(context.Background()) 60 require.NoError(t, err) 61 assert.DeepSSZEqual(t, st.InnerStateUnsafe(), savedGenesisS.InnerStateUnsafe(), "Did not retrieve saved state") 62 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), [32]byte{'C'})) 63 } 64 65 func TestStore_StatesBatchDelete(t *testing.T) { 66 db := setupDB(t) 67 ctx := context.Background() 68 numBlocks := 100 69 totalBlocks := make([]interfaces.SignedBeaconBlock, numBlocks) 70 blockRoots := make([][32]byte, 0) 71 evenBlockRoots := make([][32]byte, 0) 72 for i := 0; i < len(totalBlocks); i++ { 73 b := testutil.NewBeaconBlock() 74 b.Block.Slot = types.Slot(i) 75 totalBlocks[i] = wrapper.WrappedPhase0SignedBeaconBlock(b) 76 r, err := totalBlocks[i].Block().HashTreeRoot() 77 require.NoError(t, err) 78 st, err := testutil.NewBeaconState() 79 require.NoError(t, err) 80 require.NoError(t, st.SetSlot(types.Slot(i))) 81 require.NoError(t, db.SaveState(context.Background(), st, r)) 82 blockRoots = append(blockRoots, r) 83 if i%2 == 0 { 84 evenBlockRoots = append(evenBlockRoots, r) 85 } 86 } 87 require.NoError(t, db.SaveBlocks(ctx, totalBlocks)) 88 // We delete all even indexed states. 89 require.NoError(t, db.DeleteStates(ctx, evenBlockRoots)) 90 // When we retrieve the data, only the odd indexed state should remain. 91 for _, r := range blockRoots { 92 s, err := db.State(context.Background(), r) 93 require.NoError(t, err) 94 if s == nil { 95 continue 96 } 97 assert.Equal(t, types.Slot(1), s.Slot()%2, "State with slot %d should have been deleted", s.Slot()) 98 } 99 } 100 101 func TestStore_DeleteGenesisState(t *testing.T) { 102 db := setupDB(t) 103 ctx := context.Background() 104 105 genesisBlockRoot := [32]byte{'A'} 106 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot)) 107 st, err := testutil.NewBeaconState() 108 require.NoError(t, err) 109 require.NoError(t, st.SetSlot(100)) 110 require.NoError(t, db.SaveState(ctx, st, genesisBlockRoot)) 111 wantedErr := "cannot delete genesis, finalized, or head state" 112 assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, genesisBlockRoot)) 113 } 114 115 func TestStore_DeleteFinalizedState(t *testing.T) { 116 db := setupDB(t) 117 ctx := context.Background() 118 119 genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'}) 120 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesis)) 121 122 blk := testutil.NewBeaconBlock() 123 blk.Block.ParentRoot = genesis[:] 124 blk.Block.Slot = 100 125 126 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk))) 127 128 finalizedBlockRoot, err := blk.Block.HashTreeRoot() 129 require.NoError(t, err) 130 131 finalizedState, err := testutil.NewBeaconState() 132 require.NoError(t, err) 133 require.NoError(t, finalizedState.SetSlot(100)) 134 require.NoError(t, db.SaveState(ctx, finalizedState, finalizedBlockRoot)) 135 finalizedCheckpoint := ðpb.Checkpoint{Root: finalizedBlockRoot[:]} 136 require.NoError(t, db.SaveFinalizedCheckpoint(ctx, finalizedCheckpoint)) 137 wantedErr := "cannot delete genesis, finalized, or head state" 138 assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, finalizedBlockRoot)) 139 } 140 141 func TestStore_DeleteHeadState(t *testing.T) { 142 db := setupDB(t) 143 ctx := context.Background() 144 145 genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'}) 146 require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesis)) 147 148 blk := testutil.NewBeaconBlock() 149 blk.Block.ParentRoot = genesis[:] 150 blk.Block.Slot = 100 151 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk))) 152 153 headBlockRoot, err := blk.Block.HashTreeRoot() 154 require.NoError(t, err) 155 st, err := testutil.NewBeaconState() 156 require.NoError(t, err) 157 require.NoError(t, st.SetSlot(100)) 158 require.NoError(t, db.SaveState(ctx, st, headBlockRoot)) 159 require.NoError(t, db.SaveHeadBlockRoot(ctx, headBlockRoot)) 160 wantedErr := "cannot delete genesis, finalized, or head state" 161 assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, headBlockRoot)) 162 } 163 164 func TestStore_SaveDeleteState_CanGetHighestBelow(t *testing.T) { 165 db := setupDB(t) 166 167 b := testutil.NewBeaconBlock() 168 b.Block.Slot = 1 169 r, err := b.Block.HashTreeRoot() 170 require.NoError(t, err) 171 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 172 st, err := testutil.NewBeaconState() 173 require.NoError(t, err) 174 require.NoError(t, st.SetSlot(1)) 175 s0 := st.InnerStateUnsafe() 176 require.NoError(t, db.SaveState(context.Background(), st, r)) 177 178 b.Block.Slot = 100 179 r1, err := b.Block.HashTreeRoot() 180 require.NoError(t, err) 181 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 182 st, err = testutil.NewBeaconState() 183 require.NoError(t, err) 184 require.NoError(t, st.SetSlot(100)) 185 s1 := st.InnerStateUnsafe() 186 require.NoError(t, db.SaveState(context.Background(), st, r1)) 187 188 b.Block.Slot = 1000 189 r2, err := b.Block.HashTreeRoot() 190 require.NoError(t, err) 191 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 192 st, err = testutil.NewBeaconState() 193 require.NoError(t, err) 194 require.NoError(t, st.SetSlot(1000)) 195 s2 := st.InnerStateUnsafe() 196 197 require.NoError(t, db.SaveState(context.Background(), st, r2)) 198 199 highest, err := db.HighestSlotStatesBelow(context.Background(), 2) 200 require.NoError(t, err) 201 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), s0) 202 203 highest, err = db.HighestSlotStatesBelow(context.Background(), 101) 204 require.NoError(t, err) 205 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), s1) 206 207 highest, err = db.HighestSlotStatesBelow(context.Background(), 1001) 208 require.NoError(t, err) 209 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), s2) 210 } 211 212 func TestStore_GenesisState_CanGetHighestBelow(t *testing.T) { 213 db := setupDB(t) 214 215 genesisState, err := testutil.NewBeaconState() 216 require.NoError(t, err) 217 genesisRoot := [32]byte{'a'} 218 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot)) 219 require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot)) 220 221 b := testutil.NewBeaconBlock() 222 b.Block.Slot = 1 223 r, err := b.Block.HashTreeRoot() 224 require.NoError(t, err) 225 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 226 227 st, err := testutil.NewBeaconState() 228 require.NoError(t, err) 229 require.NoError(t, st.SetSlot(1)) 230 require.NoError(t, db.SaveState(context.Background(), st, r)) 231 232 highest, err := db.HighestSlotStatesBelow(context.Background(), 2) 233 require.NoError(t, err) 234 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), st.InnerStateUnsafe()) 235 236 highest, err = db.HighestSlotStatesBelow(context.Background(), 1) 237 require.NoError(t, err) 238 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), genesisState.InnerStateUnsafe()) 239 highest, err = db.HighestSlotStatesBelow(context.Background(), 0) 240 require.NoError(t, err) 241 assert.DeepSSZEqual(t, highest[0].InnerStateUnsafe(), genesisState.InnerStateUnsafe()) 242 } 243 244 func TestStore_CleanUpDirtyStates_AboveThreshold(t *testing.T) { 245 db := setupDB(t) 246 247 genesisState, err := testutil.NewBeaconState() 248 require.NoError(t, err) 249 genesisRoot := [32]byte{'a'} 250 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot)) 251 require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot)) 252 253 bRoots := make([][32]byte, 0) 254 slotsPerArchivedPoint := types.Slot(128) 255 prevRoot := genesisRoot 256 for i := types.Slot(1); i <= slotsPerArchivedPoint; i++ { 257 b := testutil.NewBeaconBlock() 258 b.Block.Slot = i 259 b.Block.ParentRoot = prevRoot[:] 260 r, err := b.Block.HashTreeRoot() 261 require.NoError(t, err) 262 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 263 bRoots = append(bRoots, r) 264 prevRoot = r 265 266 st, err := testutil.NewBeaconState() 267 require.NoError(t, err) 268 require.NoError(t, st.SetSlot(i)) 269 require.NoError(t, db.SaveState(context.Background(), st, r)) 270 } 271 272 require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), ðpb.Checkpoint{ 273 Root: bRoots[len(bRoots)-1][:], 274 Epoch: types.Epoch(slotsPerArchivedPoint / params.BeaconConfig().SlotsPerEpoch), 275 })) 276 require.NoError(t, db.CleanUpDirtyStates(context.Background(), slotsPerArchivedPoint)) 277 278 for i, root := range bRoots { 279 if types.Slot(i) >= slotsPerArchivedPoint.SubSlot(slotsPerArchivedPoint.Div(3)) { 280 require.Equal(t, true, db.HasState(context.Background(), root)) 281 } else { 282 require.Equal(t, false, db.HasState(context.Background(), root)) 283 } 284 } 285 } 286 287 func TestStore_CleanUpDirtyStates_Finalized(t *testing.T) { 288 db := setupDB(t) 289 290 genesisState, err := testutil.NewBeaconState() 291 require.NoError(t, err) 292 genesisRoot := [32]byte{'a'} 293 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot)) 294 require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot)) 295 296 for i := types.Slot(1); i <= params.BeaconConfig().SlotsPerEpoch; i++ { 297 b := testutil.NewBeaconBlock() 298 b.Block.Slot = i 299 r, err := b.Block.HashTreeRoot() 300 require.NoError(t, err) 301 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 302 303 st, err := testutil.NewBeaconState() 304 require.NoError(t, err) 305 require.NoError(t, st.SetSlot(i)) 306 require.NoError(t, db.SaveState(context.Background(), st, r)) 307 } 308 309 require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), ðpb.Checkpoint{Root: genesisRoot[:]})) 310 require.NoError(t, db.CleanUpDirtyStates(context.Background(), params.BeaconConfig().SlotsPerEpoch)) 311 require.Equal(t, true, db.HasState(context.Background(), genesisRoot)) 312 } 313 314 func TestStore_CleanUpDirtyStates_DontDeleteNonFinalized(t *testing.T) { 315 db := setupDB(t) 316 317 genesisState, err := testutil.NewBeaconState() 318 require.NoError(t, err) 319 genesisRoot := [32]byte{'a'} 320 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot)) 321 require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot)) 322 323 var unfinalizedRoots [][32]byte 324 for i := types.Slot(1); i <= params.BeaconConfig().SlotsPerEpoch; i++ { 325 b := testutil.NewBeaconBlock() 326 b.Block.Slot = i 327 r, err := b.Block.HashTreeRoot() 328 require.NoError(t, err) 329 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 330 unfinalizedRoots = append(unfinalizedRoots, r) 331 332 st, err := testutil.NewBeaconState() 333 require.NoError(t, err) 334 require.NoError(t, st.SetSlot(i)) 335 require.NoError(t, db.SaveState(context.Background(), st, r)) 336 } 337 338 require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), ðpb.Checkpoint{Root: genesisRoot[:]})) 339 require.NoError(t, db.CleanUpDirtyStates(context.Background(), params.BeaconConfig().SlotsPerEpoch)) 340 341 for _, rt := range unfinalizedRoots { 342 require.Equal(t, true, db.HasState(context.Background(), rt)) 343 } 344 }