diff --git a/cmd/validator/slashing-protection/import_export_test.go b/cmd/validator/slashing-protection/import_export_test.go index 9d1729a210ad..d501b68a1df4 100644 --- a/cmd/validator/slashing-protection/import_export_test.go +++ b/cmd/validator/slashing-protection/import_export_test.go @@ -35,6 +35,11 @@ func setupCliCtx( return cli.NewContext(&app, set, nil) } +// TestImportExportSlashingProtectionCli_RoundTrip imports a EIP-3076 interchange format JSON file, +// and exports it back to disk. It then compare the exported file to the original file. +// This test is only suitable for complete slashing protection history database, since minimal +// slashing protection history database will keep only the latest signed block slot / attestations, +// and thus will not be able to export the same data as the original file. func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) { numValidators := 10 outputPath := filepath.Join(t.TempDir(), "slashing-exports") @@ -59,7 +64,8 @@ func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) { require.NoError(t, err) // We create a CLI context with the required values, such as the database datadir and output directory. - validatorDB := dbTest.SetupDB(t, pubKeys) + isSlashingProtectionMinimal := false + validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) dbPath := validatorDB.DatabasePath() require.NoError(t, validatorDB.Close()) cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath) @@ -108,6 +114,11 @@ func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) { } } +// TestImportExportSlashingProtectionCli_EmptyData imports a EIP-3076 interchange format JSON file, +// and exports it back to disk. It then compare the exported file to the original file. +// This test is only suitable for complete slashing protection history database, since minimal +// slashing protection history database will keep only the latest signed block slot / attestations, +// and thus will not be able to export the same data as the original file. func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) { numValidators := 10 outputPath := filepath.Join(t.TempDir(), "slashing-exports") @@ -135,7 +146,8 @@ func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) { require.NoError(t, err) // We create a CLI context with the required values, such as the database datadir and output directory. - validatorDB := dbTest.SetupDB(t, pubKeys) + isSlashingProtectionMinimal := false + validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) dbPath := validatorDB.DatabasePath() require.NoError(t, validatorDB.Close()) cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath) diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 4c863767d937..3fdf37ddc8b0 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -72,7 +72,7 @@ func setup(t *testing.T) (*validator, *mocks, bls.SecretKey, func()) { func setupWithKey(t *testing.T, validatorKey bls.SecretKey) (*validator, *mocks, bls.SecretKey, func()) { var pubKey [fieldparams.BLSPubkeyLength]byte copy(pubKey[:], validatorKey.PublicKey().Marshal()) - valDB := testing2.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{pubKey}) + valDB := testing2.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{pubKey}, false) ctrl := gomock.NewController(t) m := &mocks{ validatorClient: validatormock.NewMockValidatorClient(ctrl), @@ -955,28 +955,32 @@ func TestGetGraffiti_Ok(t *testing.T) { } func TestGetGraffitiOrdered_Ok(t *testing.T) { - pubKey := [fieldparams.BLSPubkeyLength]byte{'a'} - valDB := testing2.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{pubKey}) - ctrl := gomock.NewController(t) - m := &mocks{ - validatorClient: validatormock.NewMockValidatorClient(ctrl), - } - m.validatorClient.EXPECT(). - ValidatorIndex(gomock.Any(), ðpb.ValidatorIndexRequest{PublicKey: pubKey[:]}). - Times(5). - Return(ðpb.ValidatorIndexResponse{Index: 2}, nil) - - v := &validator{ - db: valDB, - validatorClient: m.validatorClient, - graffitiStruct: &graffiti.Graffiti{ - Ordered: []string{"a", "b", "c"}, - Default: "d", - }, - } - for _, want := range [][]byte{bytesutil.PadTo([]byte{'a'}, 32), bytesutil.PadTo([]byte{'b'}, 32), bytesutil.PadTo([]byte{'c'}, 32), bytesutil.PadTo([]byte{'d'}, 32), bytesutil.PadTo([]byte{'d'}, 32)} { - got, err := v.getGraffiti(context.Background(), pubKey) - require.NoError(t, err) - require.DeepEqual(t, want, got) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + pubKey := [fieldparams.BLSPubkeyLength]byte{'a'} + valDB := testing2.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{pubKey}, isSlashingProtectionMinimal) + ctrl := gomock.NewController(t) + m := &mocks{ + validatorClient: validatormock.NewMockValidatorClient(ctrl), + } + m.validatorClient.EXPECT(). + ValidatorIndex(gomock.Any(), ðpb.ValidatorIndexRequest{PublicKey: pubKey[:]}). + Times(5). + Return(ðpb.ValidatorIndexResponse{Index: 2}, nil) + + v := &validator{ + db: valDB, + validatorClient: m.validatorClient, + graffitiStruct: &graffiti.Graffiti{ + Ordered: []string{"a", "b", "c"}, + Default: "d", + }, + } + for _, want := range [][]byte{bytesutil.PadTo([]byte{'a'}, 32), bytesutil.PadTo([]byte{'b'}, 32), bytesutil.PadTo([]byte{'c'}, 32), bytesutil.PadTo([]byte{'d'}, 32), bytesutil.PadTo([]byte{'d'}, 32)} { + got, err := v.getGraffiti(context.Background(), pubKey) + require.NoError(t, err) + require.DeepEqual(t, want, got) + } + }) } } diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 375d14936333..b5826c6c93d5 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -188,92 +188,100 @@ func generateMockStatusResponse(pubkeys [][]byte) *ethpb.ValidatorActivationResp } func TestWaitForChainStart_SetsGenesisInfo(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - client := validatormock.NewMockValidatorClient(ctrl) - - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - v := validator{ - validatorClient: client, - db: db, - } - - // Make sure its clean at the start. - savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background()) - require.NoError(t, err) - assert.DeepEqual(t, []byte(nil), savedGenValRoot, "Unexpected saved genesis validators root") + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := validatormock.NewMockValidatorClient(ctrl) - genesis := uint64(time.Unix(1, 0).Unix()) - genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators")) - client.EXPECT().WaitForChainStart( - gomock.Any(), - &emptypb.Empty{}, - ).Return(ðpb.ChainStartResponse{ - Started: true, - GenesisTime: genesis, - GenesisValidatorsRoot: genesisValidatorsRoot[:], - }, nil) - require.NoError(t, v.WaitForChainStart(context.Background())) - savedGenValRoot, err = db.GenesisValidatorsRoot(context.Background()) - require.NoError(t, err) + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + v := validator{ + validatorClient: client, + db: db, + } - assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validators root") - assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time") - assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil") + // Make sure its clean at the start. + savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background()) + require.NoError(t, err) + assert.DeepEqual(t, []byte(nil), savedGenValRoot, "Unexpected saved genesis validators root") + + genesis := uint64(time.Unix(1, 0).Unix()) + genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators")) + client.EXPECT().WaitForChainStart( + gomock.Any(), + &emptypb.Empty{}, + ).Return(ðpb.ChainStartResponse{ + Started: true, + GenesisTime: genesis, + GenesisValidatorsRoot: genesisValidatorsRoot[:], + }, nil) + require.NoError(t, v.WaitForChainStart(context.Background())) + savedGenValRoot, err = db.GenesisValidatorsRoot(context.Background()) + require.NoError(t, err) - // Make sure there are no errors running if it is the same data. - client.EXPECT().WaitForChainStart( - gomock.Any(), - &emptypb.Empty{}, - ).Return(ðpb.ChainStartResponse{ - Started: true, - GenesisTime: genesis, - GenesisValidatorsRoot: genesisValidatorsRoot[:], - }, nil) - require.NoError(t, v.WaitForChainStart(context.Background())) + assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validators root") + assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time") + assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil") + + // Make sure there are no errors running if it is the same data. + client.EXPECT().WaitForChainStart( + gomock.Any(), + &emptypb.Empty{}, + ).Return(ðpb.ChainStartResponse{ + Started: true, + GenesisTime: genesis, + GenesisValidatorsRoot: genesisValidatorsRoot[:], + }, nil) + require.NoError(t, v.WaitForChainStart(context.Background())) + }) + } } func TestWaitForChainStart_SetsGenesisInfo_IncorrectSecondTry(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - client := validatormock.NewMockValidatorClient(ctrl) - - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - v := validator{ - validatorClient: client, - db: db, - } - genesis := uint64(time.Unix(1, 0).Unix()) - genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators")) - client.EXPECT().WaitForChainStart( - gomock.Any(), - &emptypb.Empty{}, - ).Return(ðpb.ChainStartResponse{ - Started: true, - GenesisTime: genesis, - GenesisValidatorsRoot: genesisValidatorsRoot[:], - }, nil) - require.NoError(t, v.WaitForChainStart(context.Background())) - savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background()) - require.NoError(t, err) - - assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validators root") - assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time") - assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil") + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := validatormock.NewMockValidatorClient(ctrl) - genesisValidatorsRoot = bytesutil.ToBytes32([]byte("badvalidators")) + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + v := validator{ + validatorClient: client, + db: db, + } + genesis := uint64(time.Unix(1, 0).Unix()) + genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators")) + client.EXPECT().WaitForChainStart( + gomock.Any(), + &emptypb.Empty{}, + ).Return(ðpb.ChainStartResponse{ + Started: true, + GenesisTime: genesis, + GenesisValidatorsRoot: genesisValidatorsRoot[:], + }, nil) + require.NoError(t, v.WaitForChainStart(context.Background())) + savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background()) + require.NoError(t, err) - // Make sure there are no errors running if it is the same data. - client.EXPECT().WaitForChainStart( - gomock.Any(), - &emptypb.Empty{}, - ).Return(ðpb.ChainStartResponse{ - Started: true, - GenesisTime: genesis, - GenesisValidatorsRoot: genesisValidatorsRoot[:], - }, nil) - err = v.WaitForChainStart(context.Background()) - require.ErrorContains(t, "does not match root saved", err) + assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validators root") + assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time") + assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil") + + genesisValidatorsRoot = bytesutil.ToBytes32([]byte("badvalidators")) + + // Make sure there are no errors running if it is the same data. + client.EXPECT().WaitForChainStart( + gomock.Any(), + &emptypb.Empty{}, + ).Return(ðpb.ChainStartResponse{ + Started: true, + GenesisTime: genesis, + GenesisValidatorsRoot: genesisValidatorsRoot[:], + }, nil) + err = v.WaitForChainStart(context.Background()) + require.ErrorContains(t, "does not match root saved", err) + }) + } } func TestWaitForChainStart_ContextCanceled(t *testing.T) { @@ -947,228 +955,271 @@ func (m *doppelGangerRequestMatcher) String() string { } func TestValidator_CheckDoppelGanger(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - flgs := features.Get() - flgs.EnableDoppelGanger = true - reset := features.InitWithReset(flgs) - defer reset() - tests := []struct { - name string - validatorSetter func(t *testing.T) *validator - err string - }{ - { - name: "no doppelganger", - validatorSetter: func(t *testing.T) *validator { - client := validatormock.NewMockValidatorClient(ctrl) - km := genMockKeymanager(t, 10) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - resp := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - for _, k := range keys { - pkey := k - att := createAttestation(10, 12) - rt, err := att.Data.HashTreeRoot() + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + flgs := features.Get() + flgs.EnableDoppelGanger = true + reset := features.InitWithReset(flgs) + defer reset() + tests := []struct { + name string + validatorSetter func(t *testing.T) *validator + err string + }{ + { + name: "no doppelganger", + validatorSetter: func(t *testing.T) *validator { + client := validatormock.NewMockValidatorClient(ctrl) + km := genMockKeymanager(t, 10) + keys, err := km.FetchValidatingPublicKeys(context.Background()) assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) - resp.ValidatorRequests = append(resp.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]}) - req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]}) - } - v := &validator{ - validatorClient: client, - keyManager: km, - db: db, - } - client.EXPECT().CheckDoppelGanger( - gomock.Any(), // ctx - &doppelGangerRequestMatcher{req}, // request - ).Return(nil, nil /*err*/) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} + for _, k := range keys { + pkey := k + att := createAttestation(10, 12) + rt, err := att.Data.HashTreeRoot() + assert.NoError(t, err) + assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) + signedRoot := rt[:] + if isSlashingProtectionMinimal { + signedRoot = nil + } + req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: signedRoot}) + } + v := &validator{ + validatorClient: client, + keyManager: km, + db: db, + } + client.EXPECT().CheckDoppelGanger( + gomock.Any(), // ctx + &doppelGangerRequestMatcher{req}, // request + ).Return(nil, nil /*err*/) - return v + return v + }, }, - }, - { - name: "multiple doppelganger exists", - validatorSetter: func(t *testing.T) *validator { - client := validatormock.NewMockValidatorClient(ctrl) - km := genMockKeymanager(t, 10) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} - for i, k := range keys { - pkey := k - att := createAttestation(10, 12) - rt, err := att.Data.HashTreeRoot() + { + name: "multiple doppelganger exists", + validatorSetter: func(t *testing.T) *validator { + client := validatormock.NewMockValidatorClient(ctrl) + km := genMockKeymanager(t, 10) + keys, err := km.FetchValidatingPublicKeys(context.Background()) assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) - if i%3 == 0 { - resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} + resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} + for i, k := range keys { + pkey := k + att := createAttestation(10, 12) + rt, err := att.Data.HashTreeRoot() + assert.NoError(t, err) + assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) + if i%3 == 0 { + resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + } + + signedRoot := rt[:] + if isSlashingProtectionMinimal { + signedRoot = nil + } + + req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: signedRoot}) } - req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]}) - } - v := &validator{ - validatorClient: client, - keyManager: km, - db: db, - } - client.EXPECT().CheckDoppelGanger( - gomock.Any(), // ctx - &doppelGangerRequestMatcher{req}, // request - ).Return(resp, nil /*err*/) - return v - }, - err: "Duplicate instances exists in the network for validator keys", - }, - { - name: "single doppelganger exists", - validatorSetter: func(t *testing.T) *validator { - client := validatormock.NewMockValidatorClient(ctrl) - km := genMockKeymanager(t, 10) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} - for i, k := range keys { - pkey := k - att := createAttestation(10, 12) - rt, err := att.Data.HashTreeRoot() - assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) - if i%9 == 0 { - resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + v := &validator{ + validatorClient: client, + keyManager: km, + db: db, } - req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]}) - } - v := &validator{ - validatorClient: client, - keyManager: km, - db: db, - } - client.EXPECT().CheckDoppelGanger( - gomock.Any(), // ctx - &doppelGangerRequestMatcher{req}, // request - ).Return(resp, nil /*err*/) - return v + client.EXPECT().CheckDoppelGanger( + gomock.Any(), // ctx + &doppelGangerRequestMatcher{req}, // request + ).Return(resp, nil /*err*/) + return v + }, + err: "Duplicate instances exists in the network for validator keys", }, - err: "Duplicate instances exists in the network for validator keys", - }, - { - name: "multiple attestations saved", - validatorSetter: func(t *testing.T) *validator { - client := validatormock.NewMockValidatorClient(ctrl) - km := genMockKeymanager(t, 10) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} - attLimit := 5 - for i, k := range keys { - pkey := k - for j := 0; j < attLimit; j++ { - att := createAttestation(10+primitives.Epoch(j), 12+primitives.Epoch(j)) + { + name: "single doppelganger exists", + validatorSetter: func(t *testing.T) *validator { + client := validatormock.NewMockValidatorClient(ctrl) + km := genMockKeymanager(t, 10) + keys, err := km.FetchValidatingPublicKeys(context.Background()) + assert.NoError(t, err) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} + resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} + for i, k := range keys { + pkey := k + att := createAttestation(10, 12) rt, err := att.Data.HashTreeRoot() assert.NoError(t, err) assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) - if j == attLimit-1 { - req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]}) + if i%9 == 0 { + resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + } + + signedRoot := rt[:] + if isSlashingProtectionMinimal { + signedRoot = nil } + + req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: signedRoot}) } - if i%3 == 0 { - resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + v := &validator{ + validatorClient: client, + keyManager: km, + db: db, } - } - v := &validator{ - validatorClient: client, - keyManager: km, - db: db, - } - client.EXPECT().CheckDoppelGanger( - gomock.Any(), // ctx - &doppelGangerRequestMatcher{req}, // request - ).Return(resp, nil /*err*/) - return v + client.EXPECT().CheckDoppelGanger( + gomock.Any(), // ctx + &doppelGangerRequestMatcher{req}, // request + ).Return(resp, nil /*err*/) + return v + }, + err: "Duplicate instances exists in the network for validator keys", }, - err: "Duplicate instances exists in the network for validator keys", - }, - { - name: "no history exists", - validatorSetter: func(t *testing.T) *validator { - client := validatormock.NewMockValidatorClient(ctrl) - // Use only 1 key for deterministic order. - km := genMockKeymanager(t, 1) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} - req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} - for _, k := range keys { - resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: k[:], DuplicateExists: false}) - req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: k[:], SignedRoot: make([]byte, 32), Epoch: 0}) - } - v := &validator{ - validatorClient: client, - keyManager: km, - db: db, - } - client.EXPECT().CheckDoppelGanger( - gomock.Any(), // ctx - req, // request - ).Return(resp, nil /*err*/) - return v + { + name: "multiple attestations saved", + validatorSetter: func(t *testing.T) *validator { + client := validatormock.NewMockValidatorClient(ctrl) + km := genMockKeymanager(t, 10) + keys, err := km.FetchValidatingPublicKeys(context.Background()) + assert.NoError(t, err) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} + resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} + attLimit := 5 + for i, k := range keys { + pkey := k + for j := 0; j < attLimit; j++ { + att := createAttestation(10+primitives.Epoch(j), 12+primitives.Epoch(j)) + rt, err := att.Data.HashTreeRoot() + assert.NoError(t, err) + assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att)) + + signedRoot := rt[:] + if isSlashingProtectionMinimal { + signedRoot = nil + } + + if j == attLimit-1 { + req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: signedRoot}) + } + } + if i%3 == 0 { + resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true}) + } + } + v := &validator{ + validatorClient: client, + keyManager: km, + db: db, + } + client.EXPECT().CheckDoppelGanger( + gomock.Any(), // ctx + &doppelGangerRequestMatcher{req}, // request + ).Return(resp, nil /*err*/) + return v + }, + err: "Duplicate instances exists in the network for validator keys", }, - err: "", - }, + { + name: "no history exists", + validatorSetter: func(t *testing.T) *validator { + client := validatormock.NewMockValidatorClient(ctrl) + // Use only 1 key for deterministic order. + km := genMockKeymanager(t, 1) + keys, err := km.FetchValidatingPublicKeys(context.Background()) + assert.NoError(t, err) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + resp := ðpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}} + req := ðpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}} + for _, k := range keys { + resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{PublicKey: k[:], DuplicateExists: false}) + req.ValidatorRequests = append(req.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{PublicKey: k[:], SignedRoot: make([]byte, 32), Epoch: 0}) + } + v := &validator{ + validatorClient: client, + keyManager: km, + db: db, + } + client.EXPECT().CheckDoppelGanger( + gomock.Any(), // ctx + req, // request + ).Return(resp, nil /*err*/) + return v + }, + err: "", + }, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + v := tt.validatorSetter(t) + if err := v.CheckDoppelGanger(context.Background()); tt.err != "" { + assert.ErrorContains(t, tt.err, err) + } + }) + } } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := tt.validatorSetter(t) - if err := v.CheckDoppelGanger(context.Background()); tt.err != "" { - assert.ErrorContains(t, tt.err, err) +} +func TestValidatorAttestationsAreOrdered(t *testing.T) { + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + km := genMockKeymanager(t, 10) + keys, err := km.FetchValidatingPublicKeys(context.Background()) + assert.NoError(t, err) + db := dbTest.SetupDB(t, keys, isSlashingProtectionMinimal) + + k := keys[0] + att := createAttestation(10, 14) + rt, err := att.Data.HashTreeRoot() + assert.NoError(t, err) + assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att)) + + att = createAttestation(6, 8) + rt, err = att.Data.HashTreeRoot() + assert.NoError(t, err) + + err = db.SaveAttestationForPubKey(context.Background(), k, rt, att) + if isSlashingProtectionMinimal { + assert.ErrorContains(t, "could not sign attestation with source lower than recorded source epoch", err) + } else { + assert.NoError(t, err) + } + + att = createAttestation(10, 12) + rt, err = att.Data.HashTreeRoot() + assert.NoError(t, err) + + err = db.SaveAttestationForPubKey(context.Background(), k, rt, att) + if isSlashingProtectionMinimal { + assert.ErrorContains(t, "could not sign attestation with target lower than or equal to recorded target epoch", err) + } else { + assert.NoError(t, err) + } + + att = createAttestation(2, 3) + rt, err = att.Data.HashTreeRoot() + assert.NoError(t, err) + + err = db.SaveAttestationForPubKey(context.Background(), k, rt, att) + if isSlashingProtectionMinimal { + assert.ErrorContains(t, "could not sign attestation with source lower than recorded source epoch", err) + } else { + assert.NoError(t, err) } + + histories, err := db.AttestationHistoryForPubKey(context.Background(), k) + assert.NoError(t, err) + r := retrieveLatestRecord(histories) + assert.Equal(t, r.Target, primitives.Epoch(14)) }) } } -func TestValidatorAttestationsAreOrdered(t *testing.T) { - km := genMockKeymanager(t, 10) - keys, err := km.FetchValidatingPublicKeys(context.Background()) - assert.NoError(t, err) - db := dbTest.SetupDB(t, keys) - - k := keys[0] - att := createAttestation(10, 14) - rt, err := att.Data.HashTreeRoot() - assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att)) - - att = createAttestation(6, 8) - rt, err = att.Data.HashTreeRoot() - assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att)) - - att = createAttestation(10, 12) - rt, err = att.Data.HashTreeRoot() - assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att)) - - att = createAttestation(2, 3) - rt, err = att.Data.HashTreeRoot() - assert.NoError(t, err) - assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att)) - - histories, err := db.AttestationHistoryForPubKey(context.Background(), k) - assert.NoError(t, err) - r := retrieveLatestRecord(histories) - assert.Equal(t, r.Target, primitives.Epoch(14)) -} - func createAttestation(source, target primitives.Epoch) *ethpb.IndexedAttestation { return ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ @@ -1229,673 +1280,689 @@ func TestIsSyncCommitteeAggregator_OK(t *testing.T) { } func TestValidator_WaitForKeymanagerInitialization_web3Signer(t *testing.T) { - ctx := context.Background() - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - root := make([]byte, 32) - copy(root[2:], "a") - err := db.SaveGenesisValidatorsRoot(ctx, root) - require.NoError(t, err) - w := wallet.NewWalletForWeb3Signer() - decodedKey, err := hexutil.Decode("0xa2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820") - require.NoError(t, err) - keys := [][48]byte{ - bytesutil.ToBytes48(decodedKey), - } - v := validator{ - db: db, - useWeb: false, - wallet: w, - Web3SignerConfig: &remoteweb3signer.SetupConfig{ - BaseEndpoint: "http://localhost:8545", - ProvidedPublicKeys: keys, - }, + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + root := make([]byte, 32) + copy(root[2:], "a") + err := db.SaveGenesisValidatorsRoot(ctx, root) + require.NoError(t, err) + w := wallet.NewWalletForWeb3Signer() + decodedKey, err := hexutil.Decode("0xa2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820") + require.NoError(t, err) + keys := [][48]byte{ + bytesutil.ToBytes48(decodedKey), + } + v := validator{ + db: db, + useWeb: false, + wallet: w, + Web3SignerConfig: &remoteweb3signer.SetupConfig{ + BaseEndpoint: "http://localhost:8545", + ProvidedPublicKeys: keys, + }, + } + err = v.WaitForKeymanagerInitialization(context.Background()) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + require.NotNil(t, km) + }) } - err = v.WaitForKeymanagerInitialization(context.Background()) - require.NoError(t, err) - km, err := v.Keymanager() - require.NoError(t, err) - require.NotNil(t, km) } func TestValidator_WaitForKeymanagerInitialization_Web(t *testing.T) { - ctx := context.Background() - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - root := make([]byte, 32) - copy(root[2:], "a") - err := db.SaveGenesisValidatorsRoot(ctx, root) - require.NoError(t, err) - walletChan := make(chan *wallet.Wallet, 1) - v := validator{ - db: db, - useWeb: true, - walletInitializedFeed: &event.Feed{}, - walletInitializedChannel: walletChan, - } - wait := make(chan struct{}) - go func() { - defer close(wait) - err = v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - km, err := v.Keymanager() - require.NoError(t, err) - require.NotNil(t, km) - }() + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + root := make([]byte, 32) + copy(root[2:], "a") + err := db.SaveGenesisValidatorsRoot(ctx, root) + require.NoError(t, err) + walletChan := make(chan *wallet.Wallet, 1) + v := validator{ + db: db, + useWeb: true, + walletInitializedFeed: &event.Feed{}, + walletInitializedChannel: walletChan, + } + wait := make(chan struct{}) + go func() { + defer close(wait) + err = v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + require.NotNil(t, km) + }() - walletChan <- wallet.New(&wallet.Config{ - KeymanagerKind: keymanager.Local, - }) - <-wait + walletChan <- wallet.New(&wallet.Config{ + KeymanagerKind: keymanager.Local, + }) + <-wait + }) + } } func TestValidator_WaitForKeymanagerInitialization_Interop(t *testing.T) { - ctx := context.Background() - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - root := make([]byte, 32) - copy(root[2:], "a") - err := db.SaveGenesisValidatorsRoot(ctx, root) - require.NoError(t, err) - v := validator{ - db: db, - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 2, - Offset: 1, - }, + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + root := make([]byte, 32) + copy(root[2:], "a") + err := db.SaveGenesisValidatorsRoot(ctx, root) + require.NoError(t, err) + v := validator{ + db: db, + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 2, + Offset: 1, + }, + } + err = v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + require.NotNil(t, km) + }) } - err = v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - km, err := v.Keymanager() - require.NoError(t, err) - require.NotNil(t, km) } func TestValidator_PushProposerSettings(t *testing.T) { - ctrl := gomock.NewController(t) - ctx := context.Background() - db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - client := validatormock.NewMockValidatorClient(ctrl) - nodeClient := validatormock.NewMockNodeClient(ctrl) - defaultFeeHex := "0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9" - byteValueAddress, err := hexutil.Decode("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9") - require.NoError(t, err) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + ctrl := gomock.NewController(t) + ctx := context.Background() + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + client := validatormock.NewMockValidatorClient(ctrl) + nodeClient := validatormock.NewMockNodeClient(ctrl) + defaultFeeHex := "0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9" + byteValueAddress, err := hexutil.Decode("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9") + require.NoError(t, err) - type ExpectedValidatorRegistration struct { - FeeRecipient []byte - GasLimit uint64 - Timestamp uint64 - Pubkey []byte - } + type ExpectedValidatorRegistration struct { + FeeRecipient []byte + GasLimit uint64 + Timestamp uint64 + Pubkey []byte + } - tests := []struct { - name string - validatorSetter func(t *testing.T) *validator - feeRecipientMap map[primitives.ValidatorIndex]string - mockExpectedRequests []ExpectedValidatorRegistration - err string - logMessages []string - doesntContainLogs bool - }{ - { - name: "Happy Path proposer config not nil", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 2, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:], keys[1][:]}, - }, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, - {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, - }, - }).Return(nil, nil) - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), - }, - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: 40000000, - }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + tests := []struct { + name string + validatorSetter func(t *testing.T) *validator + feeRecipientMap map[primitives.ValidatorIndex]string + mockExpectedRequests []ExpectedValidatorRegistration + err string + logMessages []string + doesntContainLogs bool + }{ + { + name: "Happy Path proposer config not nil", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 2, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:], keys[1][:]}, + }, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, + {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, + }, + }).Return(nil, nil) + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, - GasLimit: 35000000, + GasLimit: 40000000, }, - }, - }) - require.NoError(t, err) - client.EXPECT().SubmitValidatorRegistrations( - gomock.Any(), - gomock.Any(), - ).Return(&empty.Empty{}, nil) - return &v - }, - feeRecipientMap: map[primitives.ValidatorIndex]string{ - 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", - 2: defaultFeeHex, - }, - mockExpectedRequests: []ExpectedValidatorRegistration{ - - { - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), - GasLimit: 40000000, + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 35000000, + }, + }, + }) + require.NoError(t, err) + client.EXPECT().SubmitValidatorRegistrations( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) + return &v }, - { - FeeRecipient: byteValueAddress, - GasLimit: 35000000, + feeRecipientMap: map[primitives.ValidatorIndex]string{ + 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", + 2: defaultFeeHex, }, - }, - }, - { - name: " Happy Path default doesn't send validator registration", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 2, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:], keys[1][:]}, - }, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, - {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, - }, - }).Return(nil, nil) - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + mockExpectedRequests: []ExpectedValidatorRegistration{ + + { + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), + GasLimit: 40000000, }, - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: 40000000, + { + FeeRecipient: byteValueAddress, + GasLimit: 35000000, }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + }, + }, + { + name: " Happy Path default doesn't send validator registration", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 2, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:], keys[1][:]}, + }, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, + {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, + }, + }).Return(nil, nil) + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: false, - GasLimit: 35000000, + Enabled: true, + GasLimit: 40000000, }, - }, - }) - require.NoError(t, err) - client.EXPECT().SubmitValidatorRegistrations( - gomock.Any(), - gomock.Any(), - ).Return(&empty.Empty{}, nil) - return &v - }, - feeRecipientMap: map[primitives.ValidatorIndex]string{ - 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", - 2: defaultFeeHex, - }, - mockExpectedRequests: []ExpectedValidatorRegistration{ + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: false, + GasLimit: 35000000, + }, + }, + }) + require.NoError(t, err) + client.EXPECT().SubmitValidatorRegistrations( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) + return &v + }, + feeRecipientMap: map[primitives.ValidatorIndex]string{ + 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", + 2: defaultFeeHex, + }, + mockExpectedRequests: []ExpectedValidatorRegistration{ - { - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), - GasLimit: uint64(40000000), + { + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), + GasLimit: uint64(40000000), + }, }, }, - }, - { - name: " Happy Path default doesn't send any validator registrations", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 2, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:], keys[1][:]}, - }, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, - {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, - }, - }).Return(nil, nil) - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), - }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + { + name: " Happy Path default doesn't send any validator registrations", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 2, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:], keys[1][:]}, + }, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1}, + {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2}, + }, + }).Return(nil, nil) + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, - }, - }) - require.NoError(t, err) - return &v - }, - feeRecipientMap: map[primitives.ValidatorIndex]string{ - 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", - 2: defaultFeeHex, + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + }, + }) + require.NoError(t, err) + return &v + }, + feeRecipientMap: map[primitives.ValidatorIndex]string{ + 1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9", + 2: defaultFeeHex, + }, + logMessages: []string{"will not be included in builder validator registration"}, + doesntContainLogs: true, }, - logMessages: []string{"will not be included in builder validator registration"}, - doesntContainLogs: true, - }, - { - name: " Happy Path", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 1, - Offset: 1, - }, - genesisTime: 0, - } - // set bellatrix as current epoch - params.BeaconConfig().BellatrixForkEpoch = 0 - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: nil, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + { + name: " Happy Path", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 1, + Offset: 1, }, - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: validatorType.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), + genesisTime: 0, + } + // set bellatrix as current epoch + params.BeaconConfig().BellatrixForkEpoch = 0 + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: validatorType.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), + }, }, + }) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:]}, + }, nil) + + client.EXPECT().SubmitValidatorRegistrations( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1}, + }, + }).Return(nil, nil) + return &v + }, + feeRecipientMap: map[primitives.ValidatorIndex]string{ + 1: defaultFeeHex, + }, + mockExpectedRequests: []ExpectedValidatorRegistration{ + { + FeeRecipient: byteValueAddress, + GasLimit: params.BeaconConfig().DefaultBuilderGasLimit, }, - }) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:]}, - }, nil) - - client.EXPECT().SubmitValidatorRegistrations( - gomock.Any(), - gomock.Any(), - ).Return(&empty.Empty{}, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1}, - }, - }).Return(nil, nil) - return &v - }, - feeRecipientMap: map[primitives.ValidatorIndex]string{ - 1: defaultFeeHex, - }, - mockExpectedRequests: []ExpectedValidatorRegistration{ - { - FeeRecipient: byteValueAddress, - GasLimit: params.BeaconConfig().DefaultBuilderGasLimit, }, }, - }, - { - name: " Happy Path validator index not found in cache", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 1, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: nil, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + { + name: " Happy Path validator index not found in cache", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 1, + Offset: 1, }, - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: 40000000, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 40000000, + }, }, + }) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:]}, + }, nil) + client.EXPECT().SubmitValidatorRegistrations( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1}, + }, + }).Return(nil, nil) + return &v + }, + feeRecipientMap: map[primitives.ValidatorIndex]string{ + 1: defaultFeeHex, + }, + mockExpectedRequests: []ExpectedValidatorRegistration{ + { + FeeRecipient: byteValueAddress, + GasLimit: params.BeaconConfig().DefaultBuilderGasLimit, }, - }) - require.NoError(t, err) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:]}, - }, nil) - client.EXPECT().SubmitValidatorRegistrations( - gomock.Any(), - gomock.Any(), - ).Return(&empty.Empty{}, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1}, - }, - }).Return(nil, nil) - return &v - }, - feeRecipientMap: map[primitives.ValidatorIndex]string{ - 1: defaultFeeHex, - }, - mockExpectedRequests: []ExpectedValidatorRegistration{ - { - FeeRecipient: byteValueAddress, - GasLimit: params.BeaconConfig().DefaultBuilderGasLimit, }, }, - }, - { - name: " proposer config not nil but fee recipient empty", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 1, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:]}, - }, nil) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1}, - }, - }).Return(nil, nil) - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.Address{}, - }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + { + name: " proposer config not nil but fee recipient empty", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 1, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:]}, + }, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1}, + }, + }).Return(nil, nil) + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.Address{}, }, - }, - }) - require.NoError(t, err) - return &v + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + }, + }) + require.NoError(t, err) + return &v + }, }, - }, - { - name: "Validator index not found with proposeconfig", - validatorSetter: func(t *testing.T) *validator { - - v := validator{ - validatorClient: client, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 1, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - client.EXPECT().ValidatorIndex( - gomock.Any(), // ctx - ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]}, - ).Return(nil, errors.New("could not find validator index for public key")) - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"), - }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + { + name: "Validator index not found with proposeconfig", + validatorSetter: func(t *testing.T) *validator { + + v := validator{ + validatorClient: client, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 1, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + client.EXPECT().ValidatorIndex( + gomock.Any(), // ctx + ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]}, + ).Return(nil, errors.New("could not find validator index for public key")) + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, - }, - }) - require.NoError(t, err) - return &v + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + }, + }) + require.NoError(t, err) + return &v + }, }, - }, - { - name: "register validator batch failed", - validatorSetter: func(t *testing.T) *validator { - v := validator{ - validatorClient: client, - node: nodeClient, - db: db, - pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), - signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), - useWeb: false, - interopKeysConfig: &local.InteropKeymanagerConfig{ - NumValidatorKeys: 1, - Offset: 1, - }, - } - err := v.WaitForKeymanagerInitialization(ctx) - require.NoError(t, err) - config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) - km, err := v.Keymanager() - require.NoError(t, err) - keys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) - client.EXPECT().MultipleValidatorStatus( - gomock.Any(), - gomock.Any()).Return( - ðpb.MultipleValidatorStatusResponse{ - Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, - PublicKeys: [][]byte{keys[0][:]}, - }, nil) - - config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.Address{}, - }, - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: 40000000, - }, - } - err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ - ProposeConfig: config, - DefaultConfig: &validatorserviceconfig.ProposerOption{ + { + name: "register validator batch failed", + validatorSetter: func(t *testing.T) *validator { + v := validator{ + validatorClient: client, + node: nodeClient, + db: db, + pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex), + signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1), + useWeb: false, + interopKeysConfig: &local.InteropKeymanagerConfig{ + NumValidatorKeys: 1, + Offset: 1, + }, + } + err := v.WaitForKeymanagerInitialization(ctx) + require.NoError(t, err) + config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption) + km, err := v.Keymanager() + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1) + client.EXPECT().MultipleValidatorStatus( + gomock.Any(), + gomock.Any()).Return( + ðpb.MultipleValidatorStatusResponse{ + Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}}, + PublicKeys: [][]byte{keys[0][:]}, + }, nil) + + config[keys[0]] = &validatorserviceconfig.ProposerOption{ FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipient: common.Address{}, }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, }, - }, - }) - require.NoError(t, err) - client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ - Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - {FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1}, - }, - }).Return(nil, nil) - client.EXPECT().SubmitValidatorRegistrations( - gomock.Any(), - gomock.Any(), - ).Return(&empty.Empty{}, errors.New("request failed")) - return &v + } + err = v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{ + ProposeConfig: config, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 40000000, + }, + }, + }) + require.NoError(t, err) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + {FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1}, + }, + }).Return(nil, nil) + client.EXPECT().SubmitValidatorRegistrations( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, errors.New("request failed")) + return &v + }, + err: "could not submit signed registrations to beacon node", }, - err: "could not submit signed registrations to beacon node", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - hook := logTest.NewGlobal() - v := tt.validatorSetter(t) - km, err := v.Keymanager() - require.NoError(t, err) - pubkeys, err := km.FetchValidatingPublicKeys(ctx) - require.NoError(t, err) - if tt.feeRecipientMap != nil { - feeRecipients, err := v.buildPrepProposerReqs(ctx, pubkeys) + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + hook := logTest.NewGlobal() + v := tt.validatorSetter(t) + km, err := v.Keymanager() require.NoError(t, err) - signedRegisterValidatorRequests, err := v.buildSignedRegReqs(ctx, pubkeys, km.Sign) + pubkeys, err := km.FetchValidatingPublicKeys(ctx) require.NoError(t, err) - for _, recipient := range feeRecipients { - require.Equal(t, strings.ToLower(tt.feeRecipientMap[recipient.ValidatorIndex]), strings.ToLower(hexutil.Encode(recipient.FeeRecipient))) - } - require.Equal(t, len(tt.feeRecipientMap), len(feeRecipients)) - for i, request := range tt.mockExpectedRequests { - require.Equal(t, tt.mockExpectedRequests[i].GasLimit, request.GasLimit) - require.Equal(t, hexutil.Encode(tt.mockExpectedRequests[i].FeeRecipient), hexutil.Encode(request.FeeRecipient)) + if tt.feeRecipientMap != nil { + feeRecipients, err := v.buildPrepProposerReqs(ctx, pubkeys) + require.NoError(t, err) + signedRegisterValidatorRequests, err := v.buildSignedRegReqs(ctx, pubkeys, km.Sign) + require.NoError(t, err) + for _, recipient := range feeRecipients { + require.Equal(t, strings.ToLower(tt.feeRecipientMap[recipient.ValidatorIndex]), strings.ToLower(hexutil.Encode(recipient.FeeRecipient))) + } + require.Equal(t, len(tt.feeRecipientMap), len(feeRecipients)) + for i, request := range tt.mockExpectedRequests { + require.Equal(t, tt.mockExpectedRequests[i].GasLimit, request.GasLimit) + require.Equal(t, hexutil.Encode(tt.mockExpectedRequests[i].FeeRecipient), hexutil.Encode(request.FeeRecipient)) + } + // check if Pubkeys are always unique + var unique = make(map[string]bool) + for _, request := range signedRegisterValidatorRequests { + require.Equal(t, unique[common.BytesToAddress(request.Message.Pubkey).Hex()], false) + unique[common.BytesToAddress(request.Message.Pubkey).Hex()] = true + } + require.Equal(t, len(tt.mockExpectedRequests), len(signedRegisterValidatorRequests)) + require.Equal(t, len(signedRegisterValidatorRequests), len(v.signedValidatorRegistrations)) } - // check if Pubkeys are always unique - var unique = make(map[string]bool) - for _, request := range signedRegisterValidatorRequests { - require.Equal(t, unique[common.BytesToAddress(request.Message.Pubkey).Hex()], false) - unique[common.BytesToAddress(request.Message.Pubkey).Hex()] = true + deadline := time.Now().Add(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second) + if err := v.PushProposerSettings(ctx, km, 0, deadline); tt.err != "" { + assert.ErrorContains(t, tt.err, err) } - require.Equal(t, len(tt.mockExpectedRequests), len(signedRegisterValidatorRequests)) - require.Equal(t, len(signedRegisterValidatorRequests), len(v.signedValidatorRegistrations)) - } - deadline := time.Now().Add(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second) - if err := v.PushProposerSettings(ctx, km, 0, deadline); tt.err != "" { - assert.ErrorContains(t, tt.err, err) - } - if len(tt.logMessages) > 0 { - for _, message := range tt.logMessages { - if tt.doesntContainLogs { - assert.LogsDoNotContain(t, hook, message) - } else { - assert.LogsContain(t, hook, message) + if len(tt.logMessages) > 0 { + for _, message := range tt.logMessages { + if tt.doesntContainLogs { + assert.LogsDoNotContain(t, hook, message) + } else { + assert.LogsContain(t, hook, message) + } } + } + }) + } - } - }) + db.Close() } } diff --git a/validator/db/migrate_test.go b/validator/db/migrate_test.go index 0a78c4ce5c9f..4ac3a8423fc5 100644 --- a/validator/db/migrate_test.go +++ b/validator/db/migrate_test.go @@ -21,8 +21,12 @@ func TestMigrateUp_NoDBFound(t *testing.T) { assert.ErrorContains(t, "No validator db found at path", err) } +// TestMigrateUp_OK tests that a migration up is successful. +// Migration is not needed nor supported for minimal slashing protection database. +// This, it is tested only for complete slashing protection database. func TestMigrateUp_OK(t *testing.T) { - validatorDB := dbtest.SetupDB(t, nil) + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) dbPath := validatorDB.DatabasePath() require.NoError(t, validatorDB.Close()) app := cli.App{} @@ -43,8 +47,12 @@ func TestMigrateDown_NoDBFound(t *testing.T) { assert.ErrorContains(t, "No validator db found at path", err) } +// TestMigrateUp_OK tests that a migration down is successful. +// Migration is not needed nor supported for minimal slashing protection database. +// This, it is tested only for complete slashing protection database. func TestMigrateDown_OK(t *testing.T) { - validatorDB := dbtest.SetupDB(t, nil) + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) dbPath := validatorDB.DatabasePath() require.NoError(t, validatorDB.Close()) app := cli.App{} diff --git a/validator/db/testing/BUILD.bazel b/validator/db/testing/BUILD.bazel index d189ef5c4608..88cb59e744a3 100644 --- a/validator/db/testing/BUILD.bazel +++ b/validator/db/testing/BUILD.bazel @@ -10,6 +10,7 @@ go_library( ], deps = [ "//config/fieldparams:go_default_library", + "//validator/db/filesystem:go_default_library", "//validator/db/iface:go_default_library", "//validator/db/kv:go_default_library", ], diff --git a/validator/db/testing/setup_db.go b/validator/db/testing/setup_db.go index 2dcf5713a90f..f6290fcdebc9 100644 --- a/validator/db/testing/setup_db.go +++ b/validator/db/testing/setup_db.go @@ -5,25 +5,43 @@ import ( "testing" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + "github.com/prysmaticlabs/prysm/v4/validator/db/filesystem" "github.com/prysmaticlabs/prysm/v4/validator/db/iface" "github.com/prysmaticlabs/prysm/v4/validator/db/kv" ) // SetupDB instantiates and returns a DB instance for the validator client. -func SetupDB(t testing.TB, pubkeys [][fieldparams.BLSPubkeyLength]byte) iface.ValidatorDB { - db, err := kv.NewKVStore(context.Background(), t.TempDir(), &kv.Config{ - PubKeys: pubkeys, - }) +// The `minimal` flag indicates whether the DB should be instantiated with minimal, filesystem +// slashing protection database. +func SetupDB(t testing.TB, pubkeys [][fieldparams.BLSPubkeyLength]byte, mimimal bool) iface.ValidatorDB { + var ( + db iface.ValidatorDB + err error + ) + + // Create a new DB instance. + if mimimal { + config := &filesystem.Config{PubKeys: pubkeys} + db, err = filesystem.NewStore(t.TempDir(), config) + } else { + config := &kv.Config{PubKeys: pubkeys} + db, err = kv.NewKVStore(context.Background(), t.TempDir(), config) + } + if err != nil { t.Fatalf("Failed to instantiate DB: %v", err) } + + // Cleanup the DB after the test. t.Cleanup(func() { if err := db.Close(); err != nil { t.Fatalf("Failed to close database: %v", err) } + if err := db.ClearDB(); err != nil { t.Fatalf("Failed to clear database: %v", err) } }) + return db } diff --git a/validator/node/node_test.go b/validator/node/node_test.go index 4d1f9b9348a6..777c8cb0eff0 100644 --- a/validator/node/node_test.go +++ b/validator/node/node_test.go @@ -849,80 +849,86 @@ func TestProposerSettings(t *testing.T) { wantErr: "failed to unmarshal yaml file", }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - app := cli.App{} - set := flag.NewFlagSet("test", 0) - if tt.args.proposerSettingsFlagValues.dir != "" { - set.String(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir, "") - require.NoError(t, set.Set(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir)) - } - if tt.args.proposerSettingsFlagValues.url != "" { - content, err := os.ReadFile(tt.args.proposerSettingsFlagValues.url) - require.NoError(t, err) - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) - w.Header().Set("Content-Type", "application/json") - _, err := fmt.Fprintf(w, "%s", content) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isMinimalSlashingProtectionEnabled:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + app := cli.App{} + set := flag.NewFlagSet("test", 0) + if tt.args.proposerSettingsFlagValues.dir != "" { + set.String(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir, "") + require.NoError(t, set.Set(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir)) + } + if tt.args.proposerSettingsFlagValues.url != "" { + content, err := os.ReadFile(tt.args.proposerSettingsFlagValues.url) require.NoError(t, err) - })) - defer srv.Close() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json") + _, err := fmt.Fprintf(w, "%s", content) + require.NoError(t, err) + })) + defer srv.Close() - set.String(flags.ProposerSettingsURLFlag.Name, tt.args.proposerSettingsFlagValues.url, "") - require.NoError(t, set.Set(flags.ProposerSettingsURLFlag.Name, srv.URL)) - } - if tt.args.proposerSettingsFlagValues.defaultfee != "" { - set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee, "") - require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee)) - } - if tt.args.proposerSettingsFlagValues.defaultgas != "" { - set.String(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas, "") - require.NoError(t, set.Set(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas)) - } - if tt.validatorRegistrationEnabled { - set.Bool(flags.EnableBuilderFlag.Name, true, "") - } - cliCtx := cli.NewContext(&app, set, nil) - validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - if tt.withdb != nil { - err := tt.withdb(validatorDB) - require.NoError(t, err) - } - got, err := proposerSettings(cliCtx, validatorDB) - if tt.wantErr != "" { - require.ErrorContains(t, tt.wantErr, err) - return - } - if tt.wantLog != "" { - assert.LogsContain(t, hook, - tt.wantLog, - ) - } - w := tt.want() - require.DeepEqual(t, w, got) + set.String(flags.ProposerSettingsURLFlag.Name, tt.args.proposerSettingsFlagValues.url, "") + require.NoError(t, set.Set(flags.ProposerSettingsURLFlag.Name, srv.URL)) + } + if tt.args.proposerSettingsFlagValues.defaultfee != "" { + set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee, "") + require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee)) + } + if tt.args.proposerSettingsFlagValues.defaultgas != "" { + set.String(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas, "") + require.NoError(t, set.Set(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas)) + } + if tt.validatorRegistrationEnabled { + set.Bool(flags.EnableBuilderFlag.Name, true, "") + } + cliCtx := cli.NewContext(&app, set, nil) + validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + if tt.withdb != nil { + err := tt.withdb(validatorDB) + require.NoError(t, err) + } + got, err := proposerSettings(cliCtx, validatorDB) + if tt.wantErr != "" { + require.ErrorContains(t, tt.wantErr, err) + return + } + if tt.wantLog != "" { + assert.LogsContain(t, hook, + tt.wantLog, + ) + } + w := tt.want() + require.DeepEqual(t, w, got) - }) + }) + } } } func Test_ProposerSettingsWithOnlyBuilder_DoesNotSaveInDB(t *testing.T) { - app := cli.App{} - set := flag.NewFlagSet("test", 0) - set.Bool(flags.EnableBuilderFlag.Name, true, "") - cliCtx := cli.NewContext(&app, set, nil) - validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - got, err := proposerSettings(cliCtx, validatorDB) - require.NoError(t, err) - _, err = validatorDB.ProposerSettings(cliCtx.Context) - require.ErrorContains(t, "no proposer settings found in bucket", err) - want := &validatorserviceconfig.ProposerSettings{ - DefaultConfig: &validatorserviceconfig.ProposerOption{ - BuilderConfig: &validatorserviceconfig.BuilderConfig{ - Enabled: true, - GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), - Relays: nil, - }, - }, + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + app := cli.App{} + set := flag.NewFlagSet("test", 0) + set.Bool(flags.EnableBuilderFlag.Name, true, "") + cliCtx := cli.NewContext(&app, set, nil) + validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + got, err := proposerSettings(cliCtx, validatorDB) + require.NoError(t, err) + _, err = validatorDB.ProposerSettings(cliCtx.Context) + require.ErrorContains(t, "no proposer settings found in bucket", err) + want := &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), + Relays: nil, + }, + }, + } + require.DeepEqual(t, want, got) + }) } - require.DeepEqual(t, want, got) } diff --git a/validator/rpc/handlers_keymanager_test.go b/validator/rpc/handlers_keymanager_test.go index 4f9d15a750a2..8d8ab89c1ebc 100644 --- a/validator/rpc/handlers_keymanager_test.go +++ b/validator/rpc/handlers_keymanager_test.go @@ -1047,56 +1047,58 @@ func TestServer_SetGasLimit(t *testing.T) { }, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := &mock.Validator{} - err := m.SetProposerSettings(ctx, tt.proposerSettings) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - vs, err := client.NewValidatorService(ctx, &client.Config{ - Validator: m, - ValDB: validatorDB, - }) - require.NoError(t, err) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + m := &mock.Validator{} + err := m.SetProposerSettings(ctx, tt.proposerSettings) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Validator: m, + ValDB: validatorDB, + }) + require.NoError(t, err) - s := &Server{ - validatorService: vs, - beaconNodeValidatorClient: beaconClient, - valDB: validatorDB, - } + s := &Server{ + validatorService: vs, + beaconNodeValidatorClient: beaconClient, + valDB: validatorDB, + } - if tt.beaconReturn != nil { - beaconClient.EXPECT().GetFeeRecipientByPubKey( - gomock.Any(), - gomock.Any(), - ).Return(tt.beaconReturn.resp, tt.beaconReturn.error) - } + if tt.beaconReturn != nil { + beaconClient.EXPECT().GetFeeRecipientByPubKey( + gomock.Any(), + gomock.Any(), + ).Return(tt.beaconReturn.resp, tt.beaconReturn.error) + } - request := &SetGasLimitRequest{ - GasLimit: fmt.Sprintf("%d", tt.newGasLimit), - } + request := &SetGasLimitRequest{ + GasLimit: fmt.Sprintf("%d", tt.newGasLimit), + } - var buf bytes.Buffer - err = json.NewEncoder(&buf).Encode(request) - require.NoError(t, err) + var buf bytes.Buffer + err = json.NewEncoder(&buf).Encode(request) + require.NoError(t, err) - req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/eth/v1/validator/{pubkey}/gas_limit"), &buf) - req = mux.SetURLVars(req, map[string]string{"pubkey": hexutil.Encode(tt.pubkey)}) - w := httptest.NewRecorder() - w.Body = &bytes.Buffer{} + req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/eth/v1/validator/{pubkey}/gas_limit"), &buf) + req = mux.SetURLVars(req, map[string]string{"pubkey": hexutil.Encode(tt.pubkey)}) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} - s.SetGasLimit(w, req) + s.SetGasLimit(w, req) - if tt.wantErr != "" { - assert.NotEqual(t, http.StatusOK, w.Code) - require.StringContains(t, tt.wantErr, w.Body.String()) - } else { - assert.Equal(t, http.StatusAccepted, w.Code) - for _, wantObj := range tt.w { - assert.Equal(t, wantObj.gaslimit, uint64(s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(wantObj.pubkey)].BuilderConfig.GasLimit)) + if tt.wantErr != "" { + assert.NotEqual(t, http.StatusOK, w.Code) + require.StringContains(t, tt.wantErr, w.Body.String()) + } else { + assert.Equal(t, http.StatusAccepted, w.Code) + for _, wantObj := range tt.w { + assert.Equal(t, wantObj.gaslimit, uint64(s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(wantObj.pubkey)].BuilderConfig.GasLimit)) + } } - } - }) + }) + } } } @@ -1234,40 +1236,42 @@ func TestServer_DeleteGasLimit(t *testing.T) { w: []want{}, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := &mock.Validator{} - err := m.SetProposerSettings(ctx, tt.proposerSettings) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - vs, err := client.NewValidatorService(ctx, &client.Config{ - Validator: m, - ValDB: validatorDB, - }) - require.NoError(t, err) - s := &Server{ - validatorService: vs, - valDB: validatorDB, - } - // Set up global default value for builder gas limit. - params.BeaconConfig().DefaultBuilderGasLimit = uint64(globalDefaultGasLimit) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + m := &mock.Validator{} + err := m.SetProposerSettings(ctx, tt.proposerSettings) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Validator: m, + ValDB: validatorDB, + }) + require.NoError(t, err) + s := &Server{ + validatorService: vs, + valDB: validatorDB, + } + // Set up global default value for builder gas limit. + params.BeaconConfig().DefaultBuilderGasLimit = uint64(globalDefaultGasLimit) - req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/gas_limit"), nil) - req = mux.SetURLVars(req, map[string]string{"pubkey": hexutil.Encode(tt.pubkey)}) - w := httptest.NewRecorder() - w.Body = &bytes.Buffer{} + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/gas_limit"), nil) + req = mux.SetURLVars(req, map[string]string{"pubkey": hexutil.Encode(tt.pubkey)}) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} - s.DeleteGasLimit(w, req) + s.DeleteGasLimit(w, req) - if tt.wantError != nil { - assert.StringContains(t, tt.wantError.Error(), w.Body.String()) - } else { - assert.Equal(t, http.StatusNoContent, w.Code) - } - for _, wantedObj := range tt.w { - assert.Equal(t, wantedObj.gaslimit, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(wantedObj.pubkey)].BuilderConfig.GasLimit) - } - }) + if tt.wantError != nil { + assert.StringContains(t, tt.wantError.Error(), w.Body.String()) + } else { + assert.Equal(t, http.StatusNoContent, w.Code) + } + for _, wantedObj := range tt.w { + assert.Equal(t, wantedObj.gaslimit, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(wantedObj.pubkey)].BuilderConfig.GasLimit) + } + }) + } } } @@ -1693,41 +1697,43 @@ func TestServer_FeeRecipientByPubkey(t *testing.T) { }, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := &mock.Validator{} - err := m.SetProposerSettings(ctx, tt.proposerSettings) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + m := &mock.Validator{} + err := m.SetProposerSettings(ctx, tt.proposerSettings) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) - // save a default here - vs, err := client.NewValidatorService(ctx, &client.Config{ - Validator: m, - ValDB: validatorDB, - }) - require.NoError(t, err) - s := &Server{ - validatorService: vs, - beaconNodeValidatorClient: beaconClient, - valDB: validatorDB, - } - request := &SetFeeRecipientByPubkeyRequest{ - Ethaddress: tt.args, - } + // save a default here + vs, err := client.NewValidatorService(ctx, &client.Config{ + Validator: m, + ValDB: validatorDB, + }) + require.NoError(t, err) + s := &Server{ + validatorService: vs, + beaconNodeValidatorClient: beaconClient, + valDB: validatorDB, + } + request := &SetFeeRecipientByPubkeyRequest{ + Ethaddress: tt.args, + } - var buf bytes.Buffer - err = json.NewEncoder(&buf).Encode(request) - require.NoError(t, err) + var buf bytes.Buffer + err = json.NewEncoder(&buf).Encode(request) + require.NoError(t, err) - req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), &buf) - req = mux.SetURLVars(req, map[string]string{"pubkey": pubkey}) - w := httptest.NewRecorder() - w.Body = &bytes.Buffer{} - s.SetFeeRecipientByPubkey(w, req) - assert.Equal(t, http.StatusAccepted, w.Code) + req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), &buf) + req = mux.SetURLVars(req, map[string]string{"pubkey": pubkey}) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.SetFeeRecipientByPubkey(w, req) + assert.Equal(t, http.StatusAccepted, w.Code) - assert.Equal(t, tt.want.valEthAddress, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig.FeeRecipient.Hex()) - }) + assert.Equal(t, tt.want.valEthAddress, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig.FeeRecipient.Hex()) + }) + } } } @@ -1803,29 +1809,31 @@ func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) { wantErr: false, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := &mock.Validator{} - err := m.SetProposerSettings(ctx, tt.proposerSettings) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) - vs, err := client.NewValidatorService(ctx, &client.Config{ - Validator: m, - ValDB: validatorDB, + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + m := &mock.Validator{} + err := m.SetProposerSettings(ctx, tt.proposerSettings) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Validator: m, + ValDB: validatorDB, + }) + require.NoError(t, err) + s := &Server{ + validatorService: vs, + valDB: validatorDB, + } + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil) + req = mux.SetURLVars(req, map[string]string{"pubkey": pubkey}) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.DeleteFeeRecipientByPubkey(w, req) + assert.Equal(t, http.StatusNoContent, w.Code) + assert.Equal(t, true, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig == nil) }) - require.NoError(t, err) - s := &Server{ - validatorService: vs, - valDB: validatorDB, - } - req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil) - req = mux.SetURLVars(req, map[string]string{"pubkey": pubkey}) - w := httptest.NewRecorder() - w.Body = &bytes.Buffer{} - s.DeleteFeeRecipientByPubkey(w, req) - assert.Equal(t, http.StatusNoContent, w.Code) - assert.Equal(t, true, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig == nil) - }) + } } } diff --git a/validator/slashing-protection-history/export_test.go b/validator/slashing-protection-history/export_test.go index faca5a56a062..6e00bb4d9f99 100644 --- a/validator/slashing-protection-history/export_test.go +++ b/validator/slashing-protection-history/export_test.go @@ -14,70 +14,90 @@ import ( ) func TestExportStandardProtectionJSON_EmptyGenesisRoot(t *testing.T) { - ctx := context.Background() - pubKeys := [][fieldparams.BLSPubkeyLength]byte{ - {1}, + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + pubKeys := [][fieldparams.BLSPubkeyLength]byte{ + {1}, + } + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) + _, err := ExportStandardProtectionJSON(ctx, validatorDB) + require.ErrorContains(t, "genesis validators root is empty", err) + genesisValidatorsRoot := [32]byte{1} + err = validatorDB.SaveGenesisValidatorsRoot(ctx, genesisValidatorsRoot[:]) + require.NoError(t, err) + _, err = ExportStandardProtectionJSON(ctx, validatorDB) + require.NoError(t, err) + }) } - validatorDB := dbtest.SetupDB(t, pubKeys) - _, err := ExportStandardProtectionJSON(ctx, validatorDB) - require.ErrorContains(t, "genesis validators root is empty", err) - genesisValidatorsRoot := [32]byte{1} - err = validatorDB.SaveGenesisValidatorsRoot(ctx, genesisValidatorsRoot[:]) - require.NoError(t, err) - _, err = ExportStandardProtectionJSON(ctx, validatorDB) - require.NoError(t, err) } func Test_getSignedAttestationsByPubKey(t *testing.T) { - t.Run("OK", func(t *testing.T) { - pubKeys := [][fieldparams.BLSPubkeyLength]byte{ - {1}, - } - ctx := context.Background() - validatorDB := dbtest.SetupDB(t, pubKeys) - - // No attestation history stored should return empty. - signedAttestations, err := signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) - require.NoError(t, err) - assert.Equal(t, 0, len(signedAttestations)) - - // We write a real attesting history to disk for the public key. - lowestSourceEpoch := primitives.Epoch(0) - lowestTargetEpoch := primitives.Epoch(4) - - require.NoError(t, validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{4}, createAttestation( - lowestSourceEpoch, - lowestTargetEpoch, - ))) - require.NoError(t, validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{5}, createAttestation( - lowestSourceEpoch, - lowestTargetEpoch+1, - ))) - - // We then retrieve the signed attestations and expect a correct result. - signedAttestations, err = signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) - require.NoError(t, err) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("OK/isSlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + pubKeys := [][fieldparams.BLSPubkeyLength]byte{ + {1}, + } + ctx := context.Background() + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) + + // No attestation history stored should return empty. + signedAttestations, err := signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) + require.NoError(t, err) + assert.Equal(t, 0, len(signedAttestations)) + + // We write a real attesting history to disk for the public key. + lowestSourceEpoch := primitives.Epoch(0) + lowestTargetEpoch := primitives.Epoch(4) + + require.NoError(t, validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{4}, createAttestation( + lowestSourceEpoch, + lowestTargetEpoch, + ))) + require.NoError(t, validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{5}, createAttestation( + lowestSourceEpoch, + lowestTargetEpoch+1, + ))) + + // We then retrieve the signed attestations and expect a correct result. + signedAttestations, err = signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) + require.NoError(t, err) + + wanted := []*format.SignedAttestation{ + { + SourceEpoch: "0", + TargetEpoch: "4", + SigningRoot: "0x0400000000000000000000000000000000000000000000000000000000000000", + }, + { + SourceEpoch: "0", + TargetEpoch: "5", + SigningRoot: "0x0500000000000000000000000000000000000000000000000000000000000000", + }, + } + + if isSlashingProtectionMinimal { + wanted = []*format.SignedAttestation{ + { + SourceEpoch: "0", + TargetEpoch: "5", + }, + } + } + assert.DeepEqual(t, wanted, signedAttestations) + }) + } - wanted := []*format.SignedAttestation{ - { - SourceEpoch: "0", - TargetEpoch: "4", - SigningRoot: "0x0400000000000000000000000000000000000000000000000000000000000000", - }, - { - SourceEpoch: "0", - TargetEpoch: "5", - SigningRoot: "0x0500000000000000000000000000000000000000000000000000000000000000", - }, - } - assert.DeepEqual(t, wanted, signedAttestations) - }) + // This test is specific to the old, complete slashing protection database schema bug. + // It is not needed for the new, minimal slashing protection database schema. t.Run("old_schema_bug_edge_case_genesis", func(t *testing.T) { pubKeys := [][fieldparams.BLSPubkeyLength]byte{ {1}, } ctx := context.Background() - validatorDB := dbtest.SetupDB(t, pubKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) // No attestation history stored should return empty. signedAttestations, err := signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) @@ -114,12 +134,17 @@ func Test_getSignedAttestationsByPubKey(t *testing.T) { } assert.DeepEqual(t, wanted, signedAttestations) }) + + // This test is specific to the old, complete slashing protection database schema bug. + // It is not needed for the new, minimal slashing protection database schema. t.Run("old_schema_bug_edge_case_not_genesis", func(t *testing.T) { pubKeys := [][fieldparams.BLSPubkeyLength]byte{ {1}, } ctx := context.Background() - validatorDB := dbtest.SetupDB(t, pubKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) // No attestation history stored should return empty. signedAttestations, err := signedAttestationsByPubKey(ctx, validatorDB, pubKeys[0]) @@ -164,50 +189,63 @@ func Test_getSignedAttestationsByPubKey(t *testing.T) { } func Test_getSignedBlocksByPubKey(t *testing.T) { - pubKeys := [][fieldparams.BLSPubkeyLength]byte{ - {1}, - } - ctx := context.Background() - validatorDB := dbtest.SetupDB(t, pubKeys) - - // No highest and/or lowest signed blocks will return empty. - signedBlocks, err := signedBlocksByPubKey(ctx, validatorDB, pubKeys[0]) - require.NoError(t, err) - assert.Equal(t, 0, len(signedBlocks)) - - // We mark slot 1 as proposed. - dummyRoot1 := [32]byte{1} - err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 1, dummyRoot1[:]) - require.NoError(t, err) - - // We mark slot 3 as proposed but with empty signing root. - err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 3, nil) - require.NoError(t, err) - - // We mark slot 5 as proposed. - dummyRoot2 := [32]byte{2} - err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 5, dummyRoot2[:]) - require.NoError(t, err) - - // We expect a valid proposal history containing slot 1 and slot 5 only - // when we attempt to retrieve it from disk. - signedBlocks, err = signedBlocksByPubKey(ctx, validatorDB, pubKeys[0]) - require.NoError(t, err) - wanted := []*format.SignedBlock{ - { - Slot: "1", - SigningRoot: fmt.Sprintf("%#x", dummyRoot1), - }, - { - Slot: "3", - SigningRoot: "0x0000000000000000000000000000000000000000000000000000000000000000", - }, - { - Slot: "5", - SigningRoot: fmt.Sprintf("%#x", dummyRoot2), - }, - } - for i, blk := range wanted { - assert.DeepEqual(t, blk, signedBlocks[i]) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) { + pubKeys := [][fieldparams.BLSPubkeyLength]byte{ + {1}, + } + ctx := context.Background() + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) + + // No highest and/or lowest signed blocks will return empty. + signedBlocks, err := signedBlocksByPubKey(ctx, validatorDB, pubKeys[0]) + require.NoError(t, err) + assert.Equal(t, 0, len(signedBlocks)) + + // We mark slot 1 as proposed. + dummyRoot1 := [32]byte{1} + err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 1, dummyRoot1[:]) + require.NoError(t, err) + + // We mark slot 3 as proposed but with empty signing root. + err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 3, nil) + require.NoError(t, err) + + // We mark slot 5 as proposed. + dummyRoot2 := [32]byte{2} + err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 5, dummyRoot2[:]) + require.NoError(t, err) + + // We expect a valid proposal history containing slot 1 and slot 5 only + // when we attempt to retrieve it from disk. + signedBlocks, err = signedBlocksByPubKey(ctx, validatorDB, pubKeys[0]) + require.NoError(t, err) + + wanted := []*format.SignedBlock{ + { + Slot: "1", + SigningRoot: fmt.Sprintf("%#x", dummyRoot1), + }, + { + Slot: "3", + SigningRoot: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + { + Slot: "5", + SigningRoot: fmt.Sprintf("%#x", dummyRoot2), + }, + } + + if isSlashingProtectionMinimal { + wanted = []*format.SignedBlock{ + { + Slot: "5", + }, + } + } + for i, blk := range wanted { + assert.DeepEqual(t, blk, signedBlocks[i]) + } + }) } } diff --git a/validator/slashing-protection-history/import_test.go b/validator/slashing-protection-history/import_test.go index 84c47386c0a6..0d6b65458588 100644 --- a/validator/slashing-protection-history/import_test.go +++ b/validator/slashing-protection-history/import_test.go @@ -22,149 +22,176 @@ import ( ) func TestStore_ImportInterchangeData_BadJSON(t *testing.T) { - ctx := context.Background() - validatorDB := dbtest.SetupDB(t, nil) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) - buf := bytes.NewBuffer([]byte("helloworld")) - err := ImportStandardProtectionJSON(ctx, validatorDB, buf) - require.ErrorContains(t, "could not unmarshal slashing protection JSON file", err) + buf := bytes.NewBuffer([]byte("helloworld")) + err := ImportStandardProtectionJSON(ctx, validatorDB, buf) + require.ErrorContains(t, "could not unmarshal slashing protection JSON file", err) + }) + } } func TestStore_ImportInterchangeData_NilData_FailsSilently(t *testing.T) { - hook := logTest.NewGlobal() - ctx := context.Background() - validatorDB := dbtest.SetupDB(t, nil) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + hook := logTest.NewGlobal() + ctx := context.Background() + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) - interchangeJSON := &format.EIPSlashingProtectionFormat{} - encoded, err := json.Marshal(interchangeJSON) - require.NoError(t, err) + interchangeJSON := &format.EIPSlashingProtectionFormat{} + encoded, err := json.Marshal(interchangeJSON) + require.NoError(t, err) - buf := bytes.NewBuffer(encoded) - err = ImportStandardProtectionJSON(ctx, validatorDB, buf) - require.NoError(t, err) - require.LogsContain(t, hook, "No slashing protection data to import") + buf := bytes.NewBuffer(encoded) + err = ImportStandardProtectionJSON(ctx, validatorDB, buf) + require.NoError(t, err) + require.LogsContain(t, hook, "No slashing protection data to import") + }) + } } func TestStore_ImportInterchangeData_BadFormat_PreventsDBWrites(t *testing.T) { - ctx := context.Background() - numValidators := 10 - publicKeys, err := valtest.CreateRandomPubKeys(numValidators) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + numValidators := 10 + publicKeys, err := valtest.CreateRandomPubKeys(numValidators) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) - // First we setup some mock attesting and proposal histories and create a mock - // standard slashing protection format JSON struct. - attestingHistory, proposalHistory := valtest.MockAttestingAndProposalHistories(publicKeys) - standardProtectionFormat, err := valtest.MockSlashingProtectionJSON(publicKeys, attestingHistory, proposalHistory) - require.NoError(t, err) + // First we setup some mock attesting and proposal histories and create a mock + // standard slashing protection format JSON struct. + attestingHistory, proposalHistory := valtest.MockAttestingAndProposalHistories(publicKeys) + standardProtectionFormat, err := valtest.MockSlashingProtectionJSON(publicKeys, attestingHistory, proposalHistory) + require.NoError(t, err) - // We replace a slot of one of the blocks with junk data. - standardProtectionFormat.Data[0].SignedBlocks[0].Slot = "BadSlot" + // We replace a slot of one of the blocks with junk data. + standardProtectionFormat.Data[0].SignedBlocks[0].Slot = "BadSlot" - // We encode the standard slashing protection struct into a JSON format. - blob, err := json.Marshal(standardProtectionFormat) - require.NoError(t, err) - buf := bytes.NewBuffer(blob) + // We encode the standard slashing protection struct into a JSON format. + blob, err := json.Marshal(standardProtectionFormat) + require.NoError(t, err) + buf := bytes.NewBuffer(blob) - // Next, we attempt to import it into our validator database and check that - // we obtain an error during the import process. - err = ImportStandardProtectionJSON(ctx, validatorDB, buf) - assert.NotNil(t, err) + // Next, we attempt to import it into our validator database and check that + // we obtain an error during the import process. + err = ImportStandardProtectionJSON(ctx, validatorDB, buf) + assert.NotNil(t, err) - // Next, we attempt to retrieve the attesting and proposals histories from our database and - // verify nothing was saved to the DB. If there is an error in the import process, we need to make - // sure writing is an atomic operation: either the import succeeds and saves the slashing protection - // data to our DB, or it does not. - for i := 0; i < len(publicKeys); i++ { - for _, att := range attestingHistory[i] { - indexedAtt := ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{ - Epoch: att.Source, - }, - Target: ðpb.Checkpoint{ - Epoch: att.Target, - }, - }, + // Next, we attempt to retrieve the attesting and proposals histories from our database and + // verify nothing was saved to the DB. If there is an error in the import process, we need to make + // sure writing is an atomic operation: either the import succeeds and saves the slashing protection + // data to our DB, or it does not. + for i := 0; i < len(publicKeys); i++ { + for _, att := range attestingHistory[i] { + indexedAtt := ðpb.IndexedAttestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{ + Epoch: att.Source, + }, + Target: ðpb.Checkpoint{ + Epoch: att.Target, + }, + }, + } + + if !isSlashingProtectionMinimal { + slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, publicKeys[i], []byte{}, indexedAtt) + // We expect we do not have an attesting history for each attestation + require.NoError(t, err) + require.Equal(t, kv.NotSlashable, slashingKind) + } + } + + receivedHistory, err := validatorDB.ProposalHistoryForPubKey(ctx, publicKeys[i]) + require.NoError(t, err) + require.DeepEqual( + t, + make([]*kv.Proposal, 0), + receivedHistory, + "Imported proposal signing root is different than the empty default", + ) } - slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, publicKeys[i], []byte{}, indexedAtt) - // We expect we do not have an attesting history for each attestation - require.NoError(t, err) - require.Equal(t, kv.NotSlashable, slashingKind) - } - receivedHistory, err := validatorDB.ProposalHistoryForPubKey(ctx, publicKeys[i]) - require.NoError(t, err) - require.DeepEqual( - t, - make([]*kv.Proposal, 0), - receivedHistory, - "Imported proposal signing root is different than the empty default", - ) + }) } } func TestStore_ImportInterchangeData_OK(t *testing.T) { - ctx := context.Background() - numValidators := 10 - publicKeys, err := valtest.CreateRandomPubKeys(numValidators) - require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("isSlashingProtectionMinimal=%v", isSlashingProtectionMinimal), func(t *testing.T) { + ctx := context.Background() + numValidators := 10 + publicKeys, err := valtest.CreateRandomPubKeys(numValidators) + require.NoError(t, err) + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) - // First we setup some mock attesting and proposal histories and create a mock - // standard slashing protection format JSON struct. - attestingHistory, proposalHistory := valtest.MockAttestingAndProposalHistories(publicKeys) - standardProtectionFormat, err := valtest.MockSlashingProtectionJSON(publicKeys, attestingHistory, proposalHistory) - require.NoError(t, err) + // First we setup some mock attesting and proposal histories and create a mock + // standard slashing protection format JSON struct. + attestingHistory, proposalHistory := valtest.MockAttestingAndProposalHistories(publicKeys) + standardProtectionFormat, err := valtest.MockSlashingProtectionJSON(publicKeys, attestingHistory, proposalHistory) + require.NoError(t, err) - // We encode the standard slashing protection struct into a JSON format. - blob, err := json.Marshal(standardProtectionFormat) - require.NoError(t, err) - buf := bytes.NewBuffer(blob) + // We encode the standard slashing protection struct into a JSON format. + blob, err := json.Marshal(standardProtectionFormat) + require.NoError(t, err) + buf := bytes.NewBuffer(blob) - // Next, we attempt to import it into our validator database. - err = ImportStandardProtectionJSON(ctx, validatorDB, buf) - require.NoError(t, err) + // Next, we attempt to import it into our validator database. + err = ImportStandardProtectionJSON(ctx, validatorDB, buf) + require.NoError(t, err) - // Next, we attempt to retrieve the attesting and proposals histories from our database and - // verify those indeed match the originally generated mock histories. - for i := 0; i < len(publicKeys); i++ { - for _, att := range attestingHistory[i] { - indexedAtt := ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{ - Epoch: att.Source, - }, - Target: ðpb.Checkpoint{ - Epoch: att.Target, - }, - }, - } - slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, publicKeys[i], []byte{}, indexedAtt) - // We expect we have an attesting history for the attestation and when - // attempting to verify the same att is slashable with a different signing root, - // we expect to receive a double vote slashing kind. - require.NotNil(t, err) - require.Equal(t, kv.DoubleVote, slashingKind) - } + // Next, we attempt to retrieve the attesting and proposals histories from our database and + // verify those indeed match the originally generated mock histories. + for i := 0; i < len(publicKeys); i++ { + for _, att := range attestingHistory[i] { + indexedAtt := ðpb.IndexedAttestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{ + Epoch: att.Source, + }, + Target: ðpb.Checkpoint{ + Epoch: att.Target, + }, + }, + } + // We expect we have an attesting history for the attestation and when + // attempting to verify the same att is slashable with a different signing root, + // we expect to receive a double vote slashing kind. + if isSlashingProtectionMinimal { + err := validatorDB.SaveAttestationForPubKey(ctx, publicKeys[i], [fieldparams.RootLength]byte{}, indexedAtt) + require.ErrorContains(t, "could not sign attestation", err) + } else { + slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, publicKeys[i], []byte{}, indexedAtt) + require.NotNil(t, err) + require.Equal(t, kv.DoubleVote, slashingKind) + } + } - proposals := proposalHistory[i].Proposals + if !isSlashingProtectionMinimal { + proposals := proposalHistory[i].Proposals - receivedProposalHistory, err := validatorDB.ProposalHistoryForPubKey(ctx, publicKeys[i]) - require.NoError(t, err) - rootsBySlot := make(map[primitives.Slot][]byte) - for _, proposal := range receivedProposalHistory { - rootsBySlot[proposal.Slot] = proposal.SigningRoot - } - for _, proposal := range proposals { - receivedRoot, ok := rootsBySlot[proposal.Slot] - require.DeepEqual(t, true, ok) - require.DeepEqual( - t, - receivedRoot, - proposal.SigningRoot, - "Imported proposals are different then the generated ones", - ) - } + receivedProposalHistory, err := validatorDB.ProposalHistoryForPubKey(ctx, publicKeys[i]) + require.NoError(t, err) + rootsBySlot := make(map[primitives.Slot][]byte) + for _, proposal := range receivedProposalHistory { + rootsBySlot[proposal.Slot] = proposal.SigningRoot + } + for _, proposal := range proposals { + receivedRoot, ok := rootsBySlot[proposal.Slot] + require.DeepEqual(t, true, ok) + require.DeepEqual( + t, + receivedRoot, + proposal.SigningRoot, + "Imported proposals are different then the generated ones", + ) + } + } + } + }) } } @@ -220,14 +247,16 @@ func Test_validateMetadata(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - validatorDB := dbtest.SetupDB(t, nil) - ctx := context.Background() - if err := validateMetadata(ctx, validatorDB, tt.interchangeJSON); (err != nil) != tt.wantErr { - t.Errorf("validateMetadata() error = %v, wantErr %v", err, tt.wantErr) - } + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal=%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) + ctx := context.Background() + if err := validateMetadata(ctx, validatorDB, tt.interchangeJSON); (err != nil) != tt.wantErr { + t.Errorf("validateMetadata() error = %v, wantErr %v", err, tt.wantErr) + } - }) + }) + } } } @@ -275,18 +304,20 @@ func Test_validateMetadataGenesisValidatorsRoot(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - validatorDB := dbtest.SetupDB(t, nil) - ctx := context.Background() - require.NoError(t, validatorDB.SaveGenesisValidatorsRoot(ctx, tt.dbGenesisValidatorsRoot)) - err := validateMetadata(ctx, validatorDB, tt.interchangeJSON) - if tt.wantErr { - require.ErrorContains(t, "genesis validators root doesn't match the one that is stored", err) - } else { - require.NoError(t, err) - } + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal=%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + validatorDB := dbtest.SetupDB(t, nil, isSlashingProtectionMinimal) + ctx := context.Background() + require.NoError(t, validatorDB.SaveGenesisValidatorsRoot(ctx, tt.dbGenesisValidatorsRoot)) + err := validateMetadata(ctx, validatorDB, tt.interchangeJSON) + if tt.wantErr { + require.ErrorContains(t, "genesis validators root doesn't match the one that is stored", err) + } else { + require.NoError(t, err) + } - }) + }) + } } } @@ -1040,34 +1071,36 @@ func Test_filterSlashablePubKeysFromAttestations(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - attestingHistoriesByPubKey := make(map[[fieldparams.BLSPubkeyLength]byte][]*kv.AttestationRecord) - pubKeys := make([][fieldparams.BLSPubkeyLength]byte, 0) - for pubKey := range tt.incomingAttsByPubKey { - pubKeys = append(pubKeys, pubKey) - } - validatorDB := dbtest.SetupDB(t, pubKeys) - for pubKey, signedAtts := range tt.incomingAttsByPubKey { - attestingHistory, err := transformSignedAttestations(pubKey, signedAtts) - require.NoError(t, err) - for _, att := range attestingHistory { - var signingRoot [32]byte - copy(signingRoot[:], att.SigningRoot) - - indexedAtt := createAttestation(att.Source, att.Target) - err := validatorDB.SaveAttestationForPubKey(ctx, pubKey, signingRoot, indexedAtt) + for _, isSlashingProtectionMinimal := range [...]bool{false, true} { + t.Run(fmt.Sprintf("%s/isSlashingProtectionMinimal=%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) { + attestingHistoriesByPubKey := make(map[[fieldparams.BLSPubkeyLength]byte][]*kv.AttestationRecord) + pubKeys := make([][fieldparams.BLSPubkeyLength]byte, 0) + for pubKey := range tt.incomingAttsByPubKey { + pubKeys = append(pubKeys, pubKey) + } + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) + for pubKey, signedAtts := range tt.incomingAttsByPubKey { + attestingHistory, err := transformSignedAttestations(pubKey, signedAtts) require.NoError(t, err) + for _, att := range attestingHistory { + var signingRoot [32]byte + copy(signingRoot[:], att.SigningRoot) + + indexedAtt := createAttestation(att.Source, att.Target) + err := validatorDB.SaveAttestationForPubKey(ctx, pubKey, signingRoot, indexedAtt) + require.NoError(t, err) + } } - } - got, err := filterSlashablePubKeysFromAttestations(ctx, validatorDB, attestingHistoriesByPubKey) - if (err != nil) != tt.wantErr { - t.Errorf("filterSlashablePubKeysFromAttestations() error = %v, wantErr %v", err, tt.wantErr) - return - } - for _, pubKey := range got { - ok := tt.want[pubKey] - assert.Equal(t, true, ok) - } - }) + got, err := filterSlashablePubKeysFromAttestations(ctx, validatorDB, attestingHistoriesByPubKey) + if (err != nil) != tt.wantErr { + t.Errorf("filterSlashablePubKeysFromAttestations() error = %v, wantErr %v", err, tt.wantErr) + return + } + for _, pubKey := range got { + ok := tt.want[pubKey] + assert.Equal(t, true, ok) + } + }) + } } } diff --git a/validator/slashing-protection-history/round_trip_test.go b/validator/slashing-protection-history/round_trip_test.go index 6f54c518120e..6d09e0109b04 100644 --- a/validator/slashing-protection-history/round_trip_test.go +++ b/validator/slashing-protection-history/round_trip_test.go @@ -17,12 +17,19 @@ import ( slashtest "github.com/prysmaticlabs/prysm/v4/validator/testing" ) +// TestImportExport_RoundTrip tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestImportExport_RoundTrip(t *testing.T) { ctx := context.Background() numValidators := 10 publicKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) // First we setup some mock attesting and proposal histories and create a mock // standard slashing protection format JSON struct. @@ -79,12 +86,19 @@ func TestImportExport_RoundTrip(t *testing.T) { } } +// TestImportExport_RoundTrip_SkippedAttestationEpochs tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestImportExport_RoundTrip_SkippedAttestationEpochs(t *testing.T) { ctx := context.Background() numValidators := 1 pubKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, pubKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, pubKeys, isSlashingProtectionMinimal) wanted := &format.EIPSlashingProtectionFormat{ Metadata: struct { InterchangeFormatVersion string `json:"interchange_format_version"` @@ -138,12 +152,19 @@ func TestImportExport_RoundTrip_SkippedAttestationEpochs(t *testing.T) { require.DeepEqual(t, wanted.Data, eipStandard.Data) } +// TestImportExport_FilterKeys tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestImportExport_FilterKeys(t *testing.T) { ctx := context.Background() numValidators := 10 publicKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) // First we setup some mock attesting and proposal histories and create a mock // standard slashing protection format JSON struct. @@ -176,12 +197,19 @@ func TestImportExport_FilterKeys(t *testing.T) { require.Equal(t, len(rawKeys), len(eipStandard.Data)) } +// TestImportInterchangeData_OK tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestImportInterchangeData_OK(t *testing.T) { ctx := context.Background() numValidators := 10 publicKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) // First we setup some mock attesting and proposal histories and create a mock // standard slashing protection format JSON struct. @@ -239,12 +267,19 @@ func TestImportInterchangeData_OK(t *testing.T) { } } +// TestImportInterchangeData_OK_SavesBlacklistedPublicKeys tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestImportInterchangeData_OK_SavesBlacklistedPublicKeys(t *testing.T) { ctx := context.Background() numValidators := 3 publicKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) // First we setup some mock attesting and proposal histories and create a mock // standard slashing protection format JSON struct. @@ -327,12 +362,19 @@ func TestImportInterchangeData_OK_SavesBlacklistedPublicKeys(t *testing.T) { assert.Equal(t, true, ok) } +// TestStore_ImportInterchangeData_BadFormat_PreventsDBWrites tests that we can import and export slashing protection data +// in the EIP standard format and obtain the same data we started with. +// This test is not supported for minimal slashing protection database, since +// it does not keep track of attestation and proposal histories, and thus cannot +// export the same data it imported. func TestStore_ImportInterchangeData_BadFormat_PreventsDBWrites(t *testing.T) { ctx := context.Background() numValidators := 5 publicKeys, err := slashtest.CreateRandomPubKeys(numValidators) require.NoError(t, err) - validatorDB := dbtest.SetupDB(t, publicKeys) + + isSlashingProtectionMinimal := false + validatorDB := dbtest.SetupDB(t, publicKeys, isSlashingProtectionMinimal) // First we setup some mock attesting and proposal histories and create a mock // standard slashing protection format JSON struct.