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 := &ethpb.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(), &ethpb.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(), &ethpb.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(), &ethpb.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  }