github.com/prysmaticlabs/prysm@v1.4.4/validator/db/kv/proposer_protection_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
     9  	"github.com/prysmaticlabs/prysm/shared/params"
    10  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    11  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    12  )
    13  
    14  func TestProposalHistoryForSlot_InitializesNewPubKeys(t *testing.T) {
    15  	pubkeys := [][48]byte{{30}, {25}, {20}}
    16  	db := setupDB(t, pubkeys)
    17  
    18  	for _, pub := range pubkeys {
    19  		signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pub, 0)
    20  		require.NoError(t, err)
    21  		expected := bytesutil.PadTo([]byte{}, 32)
    22  		require.DeepEqual(t, expected, signingRoot[:], "Expected proposal history slot signing root to be empty")
    23  	}
    24  }
    25  
    26  func TestNewProposalHistoryForSlot_ReturnsNilIfNoHistory(t *testing.T) {
    27  	valPubkey := [48]byte{1, 2, 3}
    28  	db := setupDB(t, [][48]byte{})
    29  
    30  	_, proposalExists, err := db.ProposalHistoryForSlot(context.Background(), valPubkey, 0)
    31  	require.NoError(t, err)
    32  	assert.Equal(t, false, proposalExists)
    33  }
    34  
    35  func TestSaveProposalHistoryForSlot_OK(t *testing.T) {
    36  	pubkey := [48]byte{3}
    37  	db := setupDB(t, [][48]byte{pubkey})
    38  
    39  	slot := types.Slot(2)
    40  
    41  	err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, slot, []byte{1})
    42  	require.NoError(t, err, "Saving proposal history failed: %v")
    43  	signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pubkey, slot)
    44  	require.NoError(t, err, "Failed to get proposal history")
    45  
    46  	require.NotNil(t, signingRoot)
    47  	require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), signingRoot[:], "Expected DB to keep object the same")
    48  }
    49  
    50  func TestNewProposalHistoryForPubKey_ReturnsEmptyIfNoHistory(t *testing.T) {
    51  	valPubkey := [48]byte{1, 2, 3}
    52  	db := setupDB(t, [][48]byte{})
    53  
    54  	proposalHistory, err := db.ProposalHistoryForPubKey(context.Background(), valPubkey)
    55  	require.NoError(t, err)
    56  	assert.DeepEqual(t, make([]*Proposal, 0), proposalHistory)
    57  }
    58  
    59  func TestSaveProposalHistoryForPubKey_OK(t *testing.T) {
    60  	pubkey := [48]byte{3}
    61  	db := setupDB(t, [][48]byte{pubkey})
    62  
    63  	slot := types.Slot(2)
    64  
    65  	root := [32]byte{1}
    66  	err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, slot, root[:])
    67  	require.NoError(t, err, "Saving proposal history failed: %v")
    68  	proposalHistory, err := db.ProposalHistoryForPubKey(context.Background(), pubkey)
    69  	require.NoError(t, err, "Failed to get proposal history")
    70  
    71  	require.NotNil(t, proposalHistory)
    72  	want := []*Proposal{
    73  		{
    74  			Slot:        slot,
    75  			SigningRoot: root[:],
    76  		},
    77  	}
    78  	require.DeepEqual(t, want[0], proposalHistory[0])
    79  }
    80  
    81  func TestSaveProposalHistoryForSlot_Overwrites(t *testing.T) {
    82  	pubkey := [48]byte{0}
    83  	tests := []struct {
    84  		signingRoot []byte
    85  	}{
    86  		{
    87  			signingRoot: bytesutil.PadTo([]byte{1}, 32),
    88  		},
    89  		{
    90  			signingRoot: bytesutil.PadTo([]byte{2}, 32),
    91  		},
    92  		{
    93  			signingRoot: bytesutil.PadTo([]byte{3}, 32),
    94  		},
    95  	}
    96  
    97  	for _, tt := range tests {
    98  		db := setupDB(t, [][48]byte{pubkey})
    99  		err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, 0, tt.signingRoot)
   100  		require.NoError(t, err, "Saving proposal history failed")
   101  		proposalHistory, err := db.ProposalHistoryForPubKey(context.Background(), pubkey)
   102  		require.NoError(t, err, "Failed to get proposal history")
   103  
   104  		require.NotNil(t, proposalHistory)
   105  		require.DeepEqual(t, tt.signingRoot, proposalHistory[0].SigningRoot, "Expected DB to keep object the same")
   106  		require.NoError(t, db.Close(), "Failed to close database")
   107  	}
   108  }
   109  
   110  func TestPruneProposalHistoryBySlot_OK(t *testing.T) {
   111  	slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
   112  	wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
   113  	pubKey := [48]byte{0}
   114  	tests := []struct {
   115  		slots        []types.Slot
   116  		storedSlots  []types.Slot
   117  		removedSlots []types.Slot
   118  	}{
   119  		{
   120  			// Go 2 epochs past pruning point.
   121  			slots:        []types.Slot{slotsPerEpoch / 2, slotsPerEpoch*5 + 6, slotsPerEpoch.Mul(uint64(wsPeriod+3)) + 8},
   122  			storedSlots:  []types.Slot{slotsPerEpoch*5 + 6, slotsPerEpoch.Mul(uint64(wsPeriod+3)) + 8},
   123  			removedSlots: []types.Slot{slotsPerEpoch / 2},
   124  		},
   125  		{
   126  			// Go 10 epochs past pruning point.
   127  			slots: []types.Slot{
   128  				slotsPerEpoch + 4,
   129  				slotsPerEpoch * 2,
   130  				slotsPerEpoch * 3,
   131  				slotsPerEpoch * 4,
   132  				slotsPerEpoch * 5,
   133  				slotsPerEpoch.Mul(uint64(wsPeriod+10)) + 8,
   134  			},
   135  			storedSlots: []types.Slot{slotsPerEpoch.Mul(uint64(wsPeriod+10)) + 8},
   136  			removedSlots: []types.Slot{
   137  				slotsPerEpoch + 4,
   138  				slotsPerEpoch * 2,
   139  				slotsPerEpoch * 3,
   140  				slotsPerEpoch * 4,
   141  				slotsPerEpoch * 5,
   142  			},
   143  		},
   144  		{
   145  			// Prune none.
   146  			slots:       []types.Slot{slotsPerEpoch + 4, slotsPerEpoch*2 + 3, slotsPerEpoch*3 + 4, slotsPerEpoch*4 + 3, slotsPerEpoch*5 + 3},
   147  			storedSlots: []types.Slot{slotsPerEpoch + 4, slotsPerEpoch*2 + 3, slotsPerEpoch*3 + 4, slotsPerEpoch*4 + 3, slotsPerEpoch*5 + 3},
   148  		},
   149  	}
   150  	signedRoot := bytesutil.PadTo([]byte{1}, 32)
   151  
   152  	for _, tt := range tests {
   153  		db := setupDB(t, [][48]byte{pubKey})
   154  		for _, slot := range tt.slots {
   155  			err := db.SaveProposalHistoryForSlot(context.Background(), pubKey, slot, signedRoot)
   156  			require.NoError(t, err, "Saving proposal history failed")
   157  		}
   158  
   159  		signingRootsBySlot := make(map[types.Slot][]byte)
   160  		proposalHistory, err := db.ProposalHistoryForPubKey(context.Background(), pubKey)
   161  		require.NoError(t, err)
   162  
   163  		for _, hist := range proposalHistory {
   164  			signingRootsBySlot[hist.Slot] = hist.SigningRoot
   165  		}
   166  
   167  		for _, slot := range tt.removedSlots {
   168  			_, ok := signingRootsBySlot[slot]
   169  			require.Equal(t, false, ok)
   170  		}
   171  		for _, slot := range tt.storedSlots {
   172  			root, ok := signingRootsBySlot[slot]
   173  			require.Equal(t, true, ok)
   174  			require.DeepEqual(t, signedRoot, root, "Unexpected difference in bytes for epoch %d", slot)
   175  		}
   176  		require.NoError(t, db.Close(), "Failed to close database")
   177  	}
   178  }
   179  
   180  func TestStore_ProposedPublicKeys(t *testing.T) {
   181  	ctx := context.Background()
   182  	validatorDB, err := NewKVStore(ctx, t.TempDir(), &Config{})
   183  	require.NoError(t, err, "Failed to instantiate DB")
   184  	t.Cleanup(func() {
   185  		require.NoError(t, validatorDB.Close(), "Failed to close database")
   186  		require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
   187  	})
   188  
   189  	keys, err := validatorDB.ProposedPublicKeys(ctx)
   190  	require.NoError(t, err)
   191  	assert.DeepEqual(t, make([][48]byte, 0), keys)
   192  
   193  	pubKey := [48]byte{1}
   194  	dummyRoot := [32]byte{}
   195  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKey, 1, dummyRoot[:])
   196  	require.NoError(t, err)
   197  
   198  	keys, err = validatorDB.ProposedPublicKeys(ctx)
   199  	require.NoError(t, err)
   200  	assert.DeepEqual(t, [][48]byte{pubKey}, keys)
   201  }
   202  
   203  func TestStore_LowestSignedProposal(t *testing.T) {
   204  	ctx := context.Background()
   205  	pubkey := [48]byte{3}
   206  	dummySigningRoot := [32]byte{}
   207  	validatorDB := setupDB(t, [][48]byte{pubkey})
   208  
   209  	_, exists, err := validatorDB.LowestSignedProposal(ctx, pubkey)
   210  	require.NoError(t, err)
   211  	require.Equal(t, false, exists)
   212  
   213  	// We save our first proposal history.
   214  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 2 /* slot */, dummySigningRoot[:])
   215  	require.NoError(t, err)
   216  
   217  	// We expect the lowest signed slot is what we just saved.
   218  	slot, exists, err := validatorDB.LowestSignedProposal(ctx, pubkey)
   219  	require.NoError(t, err)
   220  	require.Equal(t, true, exists)
   221  	assert.Equal(t, types.Slot(2), slot)
   222  
   223  	// We save a higher proposal history.
   224  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 3 /* slot */, dummySigningRoot[:])
   225  	require.NoError(t, err)
   226  
   227  	// We expect the lowest signed slot did not change.
   228  	slot, exists, err = validatorDB.LowestSignedProposal(ctx, pubkey)
   229  	require.NoError(t, err)
   230  	require.Equal(t, true, exists)
   231  	assert.Equal(t, types.Slot(2), slot)
   232  
   233  	// We save a lower proposal history.
   234  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 1 /* slot */, dummySigningRoot[:])
   235  	require.NoError(t, err)
   236  
   237  	// We expect the lowest signed slot indeed changed.
   238  	slot, exists, err = validatorDB.LowestSignedProposal(ctx, pubkey)
   239  	require.NoError(t, err)
   240  	require.Equal(t, true, exists)
   241  	assert.Equal(t, types.Slot(1), slot)
   242  }
   243  
   244  func TestStore_HighestSignedProposal(t *testing.T) {
   245  	ctx := context.Background()
   246  	pubkey := [48]byte{3}
   247  	dummySigningRoot := [32]byte{}
   248  	validatorDB := setupDB(t, [][48]byte{pubkey})
   249  
   250  	_, exists, err := validatorDB.HighestSignedProposal(ctx, pubkey)
   251  	require.NoError(t, err)
   252  	require.Equal(t, false, exists)
   253  
   254  	// We save our first proposal history.
   255  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 2 /* slot */, dummySigningRoot[:])
   256  	require.NoError(t, err)
   257  
   258  	// We expect the highest signed slot is what we just saved.
   259  	slot, exists, err := validatorDB.HighestSignedProposal(ctx, pubkey)
   260  	require.NoError(t, err)
   261  	require.Equal(t, true, exists)
   262  	assert.Equal(t, types.Slot(2), slot)
   263  
   264  	// We save a lower proposal history.
   265  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 1 /* slot */, dummySigningRoot[:])
   266  	require.NoError(t, err)
   267  
   268  	// We expect the lowest signed slot did not change.
   269  	slot, exists, err = validatorDB.HighestSignedProposal(ctx, pubkey)
   270  	require.NoError(t, err)
   271  	require.Equal(t, true, exists)
   272  	assert.Equal(t, types.Slot(2), slot)
   273  
   274  	// We save a higher proposal history.
   275  	err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 3 /* slot */, dummySigningRoot[:])
   276  	require.NoError(t, err)
   277  
   278  	// We expect the highest signed slot indeed changed.
   279  	slot, exists, err = validatorDB.HighestSignedProposal(ctx, pubkey)
   280  	require.NoError(t, err)
   281  	require.Equal(t, true, exists)
   282  	assert.Equal(t, types.Slot(3), slot)
   283  }