diff --git a/beacon-chain/core/signing/signature.go b/beacon-chain/core/signing/signature.go index 9a97be03ce79..e384e74bbc16 100644 --- a/beacon-chain/core/signing/signature.go +++ b/beacon-chain/core/signing/signature.go @@ -7,6 +7,8 @@ import ( ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) +var ErrNilRegistration = errors.New("nil signed registration") + // VerifyRegistrationSignature verifies the signature of a validator's registration. func VerifyRegistrationSignature( e types.Epoch, @@ -15,7 +17,7 @@ func VerifyRegistrationSignature( genesisRoot []byte, ) error { if sr == nil || sr.Message == nil { - return errors.New("nil signed registration") + return ErrNilRegistration } d := params.BeaconConfig().DomainApplicationBuilder diff --git a/beacon-chain/core/signing/signature_test.go b/beacon-chain/core/signing/signature_test.go index 275937354280..cc2859d73172 100644 --- a/beacon-chain/core/signing/signature_test.go +++ b/beacon-chain/core/signing/signature_test.go @@ -30,7 +30,7 @@ func TestVerifyRegistrationSignature(t *testing.T) { require.NoError(t, err) sReg := ðpb.SignedValidatorRegistrationV1{ Message: reg, - Signature: sig[:], + Signature: sig, } f := st.Fork() g := st.GenesisValidatorsRoot() @@ -38,4 +38,7 @@ func TestVerifyRegistrationSignature(t *testing.T) { sReg.Signature = []byte("bad") require.ErrorIs(t, signing.VerifyRegistrationSignature(e, f, sReg, g), signing.ErrSigFailedToVerify) + + sReg.Message = nil + require.ErrorIs(t, signing.VerifyRegistrationSignature(e, f, sReg, g), signing.ErrNilRegistration) } diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 9bd70b091f81..dd79403d4529 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -98,6 +98,7 @@ go_test( "metrics_test.go", "propose_protect_test.go", "propose_test.go", + "registration_test.go", "runner_test.go", "service_test.go", "slashing_protection_interchange_test.go", diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 725b1f064390..ac72e6667b3f 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -35,7 +35,7 @@ type mocks struct { validatorClient *mock.MockBeaconNodeValidatorClient nodeClient *mock.MockNodeClient slasherClient *mock.MockSlasherClient - signExitFunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error) + signfunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error) } type mockSignature struct{} @@ -74,7 +74,7 @@ func setupWithKey(t *testing.T, validatorKey bls.SecretKey) (*validator, *mocks, validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), nodeClient: mock.NewMockNodeClient(ctrl), slasherClient: mock.NewMockSlasherClient(ctrl), - signExitFunc: func(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) { + signfunc: func(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) { return mockSignature{}, nil }, } @@ -610,7 +610,7 @@ func TestProposeExit_ValidatorIndexFailed(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), ) assert.NotNil(t, err) @@ -634,7 +634,7 @@ func TestProposeExit_GetGenesisFailed(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), ) assert.NotNil(t, err) @@ -667,7 +667,7 @@ func TestProposeExit_DomainDataFailed(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), ) assert.NotNil(t, err) @@ -701,7 +701,7 @@ func TestProposeExit_DomainDataIsNil(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), ) assert.NotNil(t, err) @@ -738,7 +738,7 @@ func TestProposeBlock_ProposeExitFailed(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), ) assert.NotNil(t, err) @@ -775,7 +775,7 @@ func TestProposeExit_BroadcastsBlock(t *testing.T) { context.Background(), m.validatorClient, m.nodeClient, - m.signExitFunc, + m.signfunc, validatorKey.PublicKey().Marshal(), )) } diff --git a/validator/client/registration.go b/validator/client/registration.go index d0b253ee15b9..891f9a7b713a 100644 --- a/validator/client/registration.go +++ b/validator/client/registration.go @@ -15,15 +15,15 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// SubmitBuilderValidatorRegistration signs validator registration object and submits it to the beacon node. -func SubmitBuilderValidatorRegistration( +// SubmitValidatorRegistration signs validator registration object and submits it to the beacon node. +func SubmitValidatorRegistration( ctx context.Context, validatorClient ethpb.BeaconNodeValidatorClient, nodeClient ethpb.NodeClient, signer signingFunc, reg *ethpb.ValidatorRegistrationV1, ) error { - ctx, span := trace.StartSpan(ctx, "validator.ProposeBuilderValidatorRegistration") + ctx, span := trace.StartSpan(ctx, "validator.SubmitBuilderValidatorRegistration") defer span.End() genesisResponse, err := nodeClient.GetGenesis(ctx, &emptypb.Empty{}) @@ -34,7 +34,7 @@ func SubmitBuilderValidatorRegistration( secs := int64(ts.Second()) - genesisResponse.GenesisTime.Seconds currentSlot := types.Slot(uint64(secs) / params.BeaconConfig().SecondsPerSlot) - sig, err := signBuilderValidatorRegistration(ctx, currentSlot, validatorClient, signer, reg) + sig, err := signValidatorRegistration(ctx, currentSlot, validatorClient, signer, reg) if err != nil { return errors.Wrap(err, "failed to sign builder validator registration obj") } @@ -51,7 +51,7 @@ func SubmitBuilderValidatorRegistration( } // Sings validator registration obj with the proposer domain and private key. -func signBuilderValidatorRegistration( +func signValidatorRegistration( ctx context.Context, slot types.Slot, validatorClient ethpb.BeaconNodeValidatorClient, diff --git a/validator/client/registration_test.go b/validator/client/registration_test.go new file mode 100644 index 000000000000..20b7b5534598 --- /dev/null +++ b/validator/client/registration_test.go @@ -0,0 +1,141 @@ +package client + +import ( + "context" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/require" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestSubmitValidatorRegistration(t *testing.T) { + _, m, validatorKey, finish := setup(t) + defer finish() + + ctx := context.Background() + reg := ðpb.ValidatorRegistrationV1{ + FeeRecipient: bytesutil.PadTo([]byte("fee"), 20), + GasLimit: 123456, + Timestamp: uint64(time.Now().Unix()), + Pubkey: validatorKey.PublicKey().Marshal(), + } + + genesisTime := ×tamppb.Timestamp{ + Seconds: time.Now().Unix(), + } + m.nodeClient.EXPECT(). + GetGenesis(gomock.Any(), &emptypb.Empty{}). + Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + ðpb.DomainRequest{Domain: params.BeaconConfig().DomainApplicationBuilder[:], Epoch: 48038396020978354}, + ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT(). + SubmitValidatorRegistration(gomock.Any(), ðpb.SignedValidatorRegistrationV1{ + Message: reg, + Signature: params.BeaconConfig().ZeroHash[:], + }). + Return(nil, nil) + require.NoError(t, nil, SubmitValidatorRegistration(ctx, m.validatorClient, m.nodeClient, m.signfunc, reg)) +} + +func TestSubmitValidatorRegistration_InvalidDomain(t *testing.T) { + _, m, validatorKey, finish := setup(t) + defer finish() + + ctx := context.Background() + reg := ðpb.ValidatorRegistrationV1{ + FeeRecipient: bytesutil.PadTo([]byte("fee"), 20), + GasLimit: 123456, + Timestamp: uint64(time.Now().Unix()), + Pubkey: validatorKey.PublicKey().Marshal(), + } + + genesisTime := ×tamppb.Timestamp{ + Seconds: time.Now().Unix(), + } + m.nodeClient.EXPECT(). + GetGenesis(gomock.Any(), &emptypb.Empty{}). + Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + ðpb.DomainRequest{Domain: params.BeaconConfig().DomainApplicationBuilder[:], Epoch: 48038396020978354}, + ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, errors.New(domainDataErr)) + + require.ErrorContains(t, domainDataErr, SubmitValidatorRegistration(ctx, m.validatorClient, m.nodeClient, m.signfunc, reg)) +} + +func TestSubmitValidatorRegistration_CantSign(t *testing.T) { + _, m, validatorKey, finish := setup(t) + defer finish() + + ctx := context.Background() + reg := ðpb.ValidatorRegistrationV1{ + FeeRecipient: bytesutil.PadTo([]byte("fee"), 20), + GasLimit: 123456, + Timestamp: uint64(time.Now().Unix()), + Pubkey: validatorKey.PublicKey().Marshal(), + } + + genesisTime := ×tamppb.Timestamp{ + Seconds: time.Now().Unix(), + } + m.nodeClient.EXPECT(). + GetGenesis(gomock.Any(), &emptypb.Empty{}). + Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + ðpb.DomainRequest{Domain: params.BeaconConfig().DomainApplicationBuilder[:], Epoch: 48038396020978354}, + ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT(). + SubmitValidatorRegistration(gomock.Any(), ðpb.SignedValidatorRegistrationV1{ + Message: reg, + Signature: params.BeaconConfig().ZeroHash[:], + }). + Return(nil, errors.New("could not sign")) + require.ErrorContains(t, "could not submit signed registration to beacon node", SubmitValidatorRegistration(ctx, m.validatorClient, m.nodeClient, m.signfunc, reg)) +} + +func Test_signValidatorRegistration(t *testing.T) { + _, m, validatorKey, finish := setup(t) + defer finish() + + ctx := context.Background() + reg := ðpb.ValidatorRegistrationV1{ + FeeRecipient: bytesutil.PadTo([]byte("fee"), 20), + GasLimit: 123456, + Timestamp: uint64(time.Now().Unix()), + Pubkey: validatorKey.PublicKey().Marshal(), + } + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + ðpb.DomainRequest{Domain: params.BeaconConfig().DomainApplicationBuilder[:]}, + ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + _, err := signValidatorRegistration( + ctx, + 1, + m.validatorClient, m.signfunc, reg) + require.NoError(t, err) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + ðpb.DomainRequest{Domain: params.BeaconConfig().DomainApplicationBuilder[:]}, + ).Times(1).Return(nil, errors.New(domainDataErr) /*err*/) + _, err = signValidatorRegistration( + ctx, + 1, + m.validatorClient, m.signfunc, reg) + require.ErrorContains(t, domainDataErr, err) +}