package kv import ( "context" "reflect" "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" ) func TestState_CanSaveRetrieve(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) s := &pb.BeaconState{Slot: 100} r := [32]byte{'A'} if db.HasState(context.Background(), r) { t.Fatal("wanted false") } if err := db.SaveState(context.Background(), s, r); err != nil { t.Fatal(err) } if !db.HasState(context.Background(), r) { t.Fatal("wanted true") } savedS, err := db.State(context.Background(), r) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(s, savedS) { t.Error("did not retrieve saved state") } savedS, err = db.State(context.Background(), [32]byte{'B'}) if err != nil { t.Fatal(err) } if savedS != nil { t.Error("unsaved state should've been nil") } } func TestHeadState_CanSaveRetrieve(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) s := &pb.BeaconState{Slot: 100} headRoot := [32]byte{'A'} if err := db.SaveState(context.Background(), s, headRoot); err != nil { t.Fatal(err) } if err := db.SaveHeadBlockRoot(context.Background(), headRoot); err != nil { t.Fatal(err) } savedHeadS, err := db.HeadState(context.Background()) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(s, savedHeadS) { t.Error("did not retrieve saved state") } } func TestGenesisState_CanSaveRetrieve(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) s := &pb.BeaconState{Slot: 1} headRoot := [32]byte{'B'} if err := db.SaveGenesisBlockRoot(context.Background(), headRoot); err != nil { t.Fatal(err) } if err := db.SaveState(context.Background(), s, headRoot); err != nil { t.Fatal(err) } savedGenesisS, err := db.GenesisState(context.Background()) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(s, savedGenesisS) { t.Error("did not retrieve saved state") } if err := db.SaveGenesisBlockRoot(context.Background(), [32]byte{'C'}); err != nil { t.Fatal(err) } savedGenesisS, err = db.HeadState(context.Background()) if err != nil { t.Fatal(err) } if savedGenesisS != nil { t.Error("unsaved genesis state should've been nil") } } func TestStore_StatesBatchDelete(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() numBlocks := 100 totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks) blockRoots := make([][32]byte, 0) evenBlockRoots := make([][32]byte, 0) for i := 0; i < len(totalBlocks); i++ { totalBlocks[i] = ðpb.SignedBeaconBlock{ Block: ðpb.BeaconBlock{ Slot: uint64(i), ParentRoot: []byte("parent"), }, } r, err := ssz.HashTreeRoot(totalBlocks[i].Block) if err != nil { t.Fatal(err) } if err := db.SaveState(context.Background(), &pb.BeaconState{Slot: uint64(i)}, r); err != nil { t.Fatal(err) } blockRoots = append(blockRoots, r) if i%2 == 0 { evenBlockRoots = append(evenBlockRoots, r) } } if err := db.SaveBlocks(ctx, totalBlocks); err != nil { t.Fatal(err) } // We delete all even indexed states. if err := db.DeleteStates(ctx, evenBlockRoots); err != nil { t.Fatal(err) } // When we retrieve the data, only the odd indexed state should remain. for _, r := range blockRoots { s, err := db.State(context.Background(), r) if err != nil { t.Fatal(err) } if s == nil { continue } if s.Slot%2 == 0 { t.Errorf("State with slot %d should have been deleted", s.Slot) } } } func TestStore_DeleteGenesisState(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() genesisBlockRoot := [32]byte{'A'} if err := db.SaveGenesisBlockRoot(ctx, genesisBlockRoot); err != nil { t.Fatal(err) } genesisState := &pb.BeaconState{Slot: 100} if err := db.SaveState(ctx, genesisState, genesisBlockRoot); err != nil { t.Fatal(err) } wantedErr := "cannot delete genesis, finalized, or head state" if err := db.DeleteState(ctx, genesisBlockRoot); err.Error() != wantedErr { t.Error("Did not receive wanted error") } } func TestStore_DeleteFinalizedState(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'}) if err := db.SaveGenesisBlockRoot(ctx, genesis); err != nil { t.Fatal(err) } blk := ðpb.SignedBeaconBlock{ Block: ðpb.BeaconBlock{ ParentRoot: genesis[:], Slot: 100, }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } finalizedBlockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } finalizedState := &pb.BeaconState{Slot: 100} if err := db.SaveState(ctx, finalizedState, finalizedBlockRoot); err != nil { t.Fatal(err) } finalizedCheckpoint := ðpb.Checkpoint{Root: finalizedBlockRoot[:]} if err := db.SaveFinalizedCheckpoint(ctx, finalizedCheckpoint); err != nil { t.Fatal(err) } wantedErr := "cannot delete genesis, finalized, or head state" if err := db.DeleteState(ctx, finalizedBlockRoot); err.Error() != wantedErr { t.Error("Did not receive wanted error") } } func TestStore_DeleteHeadState(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'}) if err := db.SaveGenesisBlockRoot(ctx, genesis); err != nil { t.Fatal(err) } blk := ðpb.SignedBeaconBlock{ Block: ðpb.BeaconBlock{ ParentRoot: genesis[:], Slot: 100, }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } headBlockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } headState := &pb.BeaconState{Slot: 100} if err := db.SaveState(ctx, headState, headBlockRoot); err != nil { t.Fatal(err) } if err := db.SaveHeadBlockRoot(ctx, headBlockRoot); err != nil { t.Fatal(err) } wantedErr := "cannot delete genesis, finalized, or head state" if err := db.DeleteState(ctx, headBlockRoot); err.Error() != wantedErr { t.Error("Did not receive wanted error") } }