diff --git a/beacon-chain/rpc/eth/validator/BUILD.bazel b/beacon-chain/rpc/eth/validator/BUILD.bazel index 261d02c360ba..90fbb2e7fe8f 100644 --- a/beacon-chain/rpc/eth/validator/BUILD.bazel +++ b/beacon-chain/rpc/eth/validator/BUILD.bazel @@ -30,6 +30,8 @@ go_library( "//proto/migration:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//time/slots:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", @@ -82,6 +84,7 @@ go_test( "//testing/require:go_default_library", "//testing/util:go_default_library", "//time/slots:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/beacon-chain/rpc/eth/validator/validator.go b/beacon-chain/rpc/eth/validator/validator.go index 5e43de1b2ef7..28b0ba78c9b0 100644 --- a/beacon-chain/rpc/eth/validator/validator.go +++ b/beacon-chain/rpc/eth/validator/validator.go @@ -3,10 +3,13 @@ package validator import ( "bytes" "context" + "fmt" "sort" "strconv" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" @@ -325,11 +328,26 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR return nil, status.Error(codes.InvalidArgument, "Unsupported block type") } -// PrepareBeaconProposer -- +// PrepareBeaconProposer caches and updates the fee recipient for the given proposer. func (vs *Server) PrepareBeaconProposer( - _ context.Context, _ *ethpbv1.PrepareBeaconProposerRequest, + ctx context.Context, request *ethpbv1.PrepareBeaconProposerRequest, ) (*emptypb.Empty, error) { - return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented") + _, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer") + defer span.End() + var FeeRecipients []common.Address + var ValidatorIndices []types.ValidatorIndex + for _, recipientContainer := range request.Recipients { + recipient := hexutil.Encode(recipientContainer.FeeRecipient) + if !common.IsHexAddress(recipient) { + return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient)) + } + FeeRecipients = append(FeeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient)) + ValidatorIndices = append(ValidatorIndices, recipientContainer.ValidatorIndex) + } + if err := vs.V1Alpha1Server.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, ValidatorIndices, FeeRecipients); err != nil { + return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err) + } + return &emptypb.Empty{}, nil } // ProduceAttestationData requests that the beacon node produces attestation data for diff --git a/beacon-chain/rpc/eth/validator/validator_test.go b/beacon-chain/rpc/eth/validator/validator_test.go index fcfefe58dbe2..af86ce7c9fd2 100644 --- a/beacon-chain/rpc/eth/validator/validator_test.go +++ b/beacon-chain/rpc/eth/validator/validator_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/go-bitfield" mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" @@ -2011,3 +2012,65 @@ func TestSubmitContributionAndProofs(t *testing.T) { require.DeepEqual(t, expectedContributions, savedMsgs) }) } + +func TestPrepareBeaconProposer(t *testing.T) { + type args struct { + request *ethpbv1.PrepareBeaconProposerRequest + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "Happy Path", + args: args{ + request: ðpbv1.PrepareBeaconProposerRequest{ + Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + ValidatorIndex: 1, + }, + }, + }, + }, + wantErr: "", + }, + { + name: "invalid fee recipient length", + args: args{ + request: ðpbv1.PrepareBeaconProposerRequest{ + Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength), + ValidatorIndex: 1, + }, + }, + }, + }, + wantErr: "Invalid fee recipient address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := dbutil.SetupDB(t) + ctx := context.Background() + v1Server := &v1alpha1validator.Server{ + BeaconDB: db, + } + server := &Server{ + V1Alpha1Server: v1Server, + } + _, err := server.PrepareBeaconProposer(ctx, tt.args.request) + if tt.wantErr != "" { + require.ErrorContains(t, tt.wantErr, err) + return + } + require.NoError(t, err) + address, err := server.V1Alpha1Server.BeaconDB.FeeRecipientByValidatorID(ctx, 1) + require.NoError(t, err) + require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address) + }) + } + +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel b/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel index 2137394e15e1..9268f31ebb77 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel @@ -75,6 +75,7 @@ go_library( "//runtime/version:go_default_library", "//time:go_default_library", "//time/slots:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_ferranbt_fastssz//:go_default_library", "@com_github_holiman_uint256//:go_default_library", diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index a5f29b24d3b4..98e9f92aa101 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -6,8 +6,11 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" emptypb "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block" "github.com/prysmaticlabs/prysm/beacon-chain/core/transition" @@ -116,11 +119,26 @@ func (vs *Server) ProposeBlock(ctx context.Context, rBlk *ethpb.SignedBeaconBloc return vs.proposeGenericBeaconBlock(ctx, blk) } -// PrepareBeaconProposer -- +// PrepareBeaconProposer caches and updates the fee recipient for the given proposer. func (vs *Server) PrepareBeaconProposer( - _ context.Context, _ *ethpb.PrepareBeaconProposerRequest, + ctx context.Context, request *ethpb.PrepareBeaconProposerRequest, ) (*emptypb.Empty, error) { - return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented") + _, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer") + defer span.End() + var FeeRecipients []common.Address + var ValidatorIndices []types.ValidatorIndex + for _, recipientContainer := range request.Recipients { + recipient := hexutil.Encode(recipientContainer.FeeRecipient) + if !common.IsHexAddress(recipient) { + return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient)) + } + FeeRecipients = append(FeeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient)) + ValidatorIndices = append(ValidatorIndices, recipientContainer.ValidatorIndex) + } + if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, ValidatorIndices, FeeRecipients); err != nil { + return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err) + } + return &emptypb.Empty{}, nil } func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk block.SignedBeaconBlock) (*ethpb.ProposeResponse, error) { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go index 876732b3f60f..cb4d8d6fda2c 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go @@ -2431,6 +2431,63 @@ func TestProposer_GetSyncAggregate_OK(t *testing.T) { require.DeepEqual(t, bitfield.NewBitvector512(), aggregate.SyncCommitteeBits) } +func TestProposer_PrepareBeaconProposer(t *testing.T) { + type args struct { + request *ethpb.PrepareBeaconProposerRequest + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "Happy Path", + args: args{ + request: ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + ValidatorIndex: 1, + }, + }, + }, + }, + wantErr: "", + }, + { + name: "invalid fee recipient length", + args: args{ + request: ðpb.PrepareBeaconProposerRequest{ + Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength), + ValidatorIndex: 1, + }, + }, + }, + }, + wantErr: "Invalid fee recipient address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := dbutil.SetupDB(t) + ctx := context.Background() + proposerServer := &Server{BeaconDB: db} + _, err := proposerServer.PrepareBeaconProposer(ctx, tt.args.request) + if tt.wantErr != "" { + require.ErrorContains(t, tt.wantErr, err) + return + } + require.NoError(t, err) + address, err := proposerServer.BeaconDB.FeeRecipientByValidatorID(ctx, 1) + require.NoError(t, err) + require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address) + + }) + } +} + func majorityVoteBoundaryTime(slot types.Slot) (uint64, uint64) { slots := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod)) slotStartTime := uint64(mockPOW.GenesisTime) + uint64((slot - (slot % (slots))).Mul(params.BeaconConfig().SecondsPerSlot))