mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
HTTP Validator API: /eth/v1/validator/{pubkey}/feerecipient (#13085)
* migrating fee recipient endpoints to pure http implementation * fixing linting * fixing type name * fixing after merging develop * fixing linting and tests * Update validator/rpc/structs.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/rpc/structs.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/rpc/structs.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -17,7 +17,6 @@ func (f *ValidatorEndpointFactory) IsNil() bool {
|
||||
func (*ValidatorEndpointFactory) Paths() []string {
|
||||
return []string{
|
||||
"/eth/v1/keystores",
|
||||
"/eth/v1/validator/{pubkey}/feerecipient",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +30,6 @@ func (*ValidatorEndpointFactory) Create(path string) (*apimiddleware.Endpoint, e
|
||||
endpoint.PostResponse = &ImportKeystoresResponseJson{}
|
||||
endpoint.DeleteRequest = &DeleteKeystoresRequestJson{}
|
||||
endpoint.DeleteResponse = &DeleteKeystoresResponseJson{}
|
||||
case "/eth/v1/validator/{pubkey}/feerecipient":
|
||||
endpoint.GetResponse = &GetFeeRecipientByPubkeyResponseJson{}
|
||||
endpoint.PostRequest = &SetFeeRecipientByPubkeyRequestJson{}
|
||||
endpoint.DeleteRequest = &DeleteFeeRecipientByPubkeyRequestJson{}
|
||||
default:
|
||||
return nil, errors.New("invalid path")
|
||||
}
|
||||
|
||||
@@ -32,20 +32,3 @@ type DeleteKeystoresResponseJson struct {
|
||||
Statuses []*StatusJson `json:"data"`
|
||||
SlashingProtection string `json:"slashing_protection"`
|
||||
}
|
||||
|
||||
type FeeRecipientJson struct {
|
||||
Pubkey string `json:"pubkey" hex:"true"`
|
||||
Ethaddress string `json:"ethaddress" address:"true"`
|
||||
}
|
||||
|
||||
type GetFeeRecipientByPubkeyResponseJson struct {
|
||||
Data *FeeRecipientJson `json:"data"`
|
||||
}
|
||||
|
||||
type SetFeeRecipientByPubkeyRequestJson struct {
|
||||
Ethaddress string `json:"ethaddress" hex:"true"`
|
||||
}
|
||||
|
||||
type DeleteFeeRecipientByPubkeyRequestJson struct {
|
||||
Pubkey string `json:"pubkey" hex:"true"`
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
@@ -242,6 +243,178 @@ func (s *Server) DeleteRemoteKeys(w http.ResponseWriter, r *http.Request) {
|
||||
http2.WriteJson(w, RemoteKeysResponse{Data: deleter.DeletePublicKeys(req.Pubkeys)})
|
||||
}
|
||||
|
||||
// ListFeeRecipientByPubkey returns the public key to eth address mapping object to the end user.
|
||||
func (s *Server) ListFeeRecipientByPubkey(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.ListFeeRecipientByPubkey")
|
||||
defer span.End()
|
||||
|
||||
if s.validatorService == nil {
|
||||
http2.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
rawPubkey := mux.Vars(r)["pubkey"]
|
||||
if rawPubkey == "" {
|
||||
http2.HandleError(w, "pubkey is required in URL params", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pubkey, valid := shared.ValidateHex(w, "pubkey", rawPubkey, fieldparams.BLSPubkeyLength)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
finalResp := &GetFeeRecipientByPubkeyResponse{
|
||||
Data: &FeeRecipient{
|
||||
Pubkey: rawPubkey,
|
||||
},
|
||||
}
|
||||
|
||||
proposerSettings := s.validatorService.ProposerSettings()
|
||||
|
||||
// If fee recipient is defined for this specific pubkey in proposer configuration, use it
|
||||
if proposerSettings != nil && proposerSettings.ProposeConfig != nil {
|
||||
proposerOption, found := proposerSettings.ProposeConfig[bytesutil.ToBytes48(pubkey)]
|
||||
|
||||
if found && proposerOption.FeeRecipientConfig != nil {
|
||||
finalResp.Data.Ethaddress = proposerOption.FeeRecipientConfig.FeeRecipient.String()
|
||||
http2.WriteJson(w, finalResp)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If fee recipient is defined in default configuration, use it
|
||||
if proposerSettings != nil && proposerSettings.DefaultConfig != nil && proposerSettings.DefaultConfig.FeeRecipientConfig != nil {
|
||||
finalResp.Data.Ethaddress = proposerSettings.DefaultConfig.FeeRecipientConfig.FeeRecipient.String()
|
||||
http2.WriteJson(w, finalResp)
|
||||
return
|
||||
}
|
||||
|
||||
http2.HandleError(w, "No fee recipient set", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// SetFeeRecipientByPubkey updates the eth address mapped to the public key.
|
||||
func (s *Server) SetFeeRecipientByPubkey(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.SetFeeRecipientByPubkey")
|
||||
defer span.End()
|
||||
|
||||
if s.validatorService == nil {
|
||||
http2.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
rawPubkey := mux.Vars(r)["pubkey"]
|
||||
if rawPubkey == "" {
|
||||
http2.HandleError(w, "pubkey is required in URL params", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pubkey, valid := shared.ValidateHex(w, "pubkey", rawPubkey, fieldparams.BLSPubkeyLength)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
var req SetFeeRecipientByPubkeyRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ethAddress, valid := shared.ValidateHex(w, "Ethereum Address", req.Ethaddress, fieldparams.FeeRecipientLength)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
feeRecipient := common.BytesToAddress(ethAddress)
|
||||
settings := s.validatorService.ProposerSettings()
|
||||
switch {
|
||||
case settings == nil:
|
||||
settings = &validatorServiceConfig.ProposerSettings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
|
||||
bytesutil.ToBytes48(pubkey): {
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: nil,
|
||||
},
|
||||
},
|
||||
DefaultConfig: nil,
|
||||
}
|
||||
case settings.ProposeConfig == nil:
|
||||
var builderConfig *validatorServiceConfig.BuilderConfig
|
||||
if settings.DefaultConfig != nil && settings.DefaultConfig.BuilderConfig != nil {
|
||||
builderConfig = settings.DefaultConfig.BuilderConfig.Clone()
|
||||
}
|
||||
settings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
|
||||
bytesutil.ToBytes48(pubkey): {
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: builderConfig,
|
||||
},
|
||||
}
|
||||
default:
|
||||
proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(pubkey)]
|
||||
if found && proposerOption != nil {
|
||||
proposerOption.FeeRecipientConfig = &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
}
|
||||
} else {
|
||||
var builderConfig = &validatorServiceConfig.BuilderConfig{}
|
||||
if settings.DefaultConfig != nil && settings.DefaultConfig.BuilderConfig != nil {
|
||||
builderConfig = settings.DefaultConfig.BuilderConfig.Clone()
|
||||
}
|
||||
settings.ProposeConfig[bytesutil.ToBytes48(pubkey)] = &validatorServiceConfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: builderConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
// save the settings
|
||||
if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil {
|
||||
http2.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// override the 200 success with 202 according to the specs
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
// DeleteFeeRecipientByPubkey updates the eth address mapped to the public key to the default fee recipient listed
|
||||
func (s *Server) DeleteFeeRecipientByPubkey(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.DeleteFeeRecipientByPubkey")
|
||||
defer span.End()
|
||||
|
||||
if s.validatorService == nil {
|
||||
http2.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
rawPubkey := mux.Vars(r)["pubkey"]
|
||||
if rawPubkey == "" {
|
||||
http2.HandleError(w, "pubkey is required in URL params", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pubkey, valid := shared.ValidateHex(w, "pubkey", rawPubkey, fieldparams.BLSPubkeyLength)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
settings := s.validatorService.ProposerSettings()
|
||||
|
||||
if settings != nil && settings.ProposeConfig != nil {
|
||||
proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(pubkey)]
|
||||
if found {
|
||||
proposerOption.FeeRecipientConfig = nil
|
||||
}
|
||||
}
|
||||
|
||||
// save the settings
|
||||
if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil {
|
||||
http2.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// override the 200 success with 204 according to the specs
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetGasLimit returns the gas limit measured in gwei defined for the custom mev builder by public key
|
||||
func (s *Server) GetGasLimit(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.GetGasLimit")
|
||||
@@ -292,7 +465,6 @@ func (s *Server) SetGasLimit(w http.ResponseWriter, r *http.Request) {
|
||||
http2.HandleError(w, "Validator service not ready", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
rawPubkey := mux.Vars(r)["pubkey"]
|
||||
if rawPubkey == "" {
|
||||
http2.HandleError(w, "pubkey is required in URL params", http.StatusBadRequest)
|
||||
@@ -352,7 +524,6 @@ func (s *Server) SetGasLimit(w http.ResponseWriter, r *http.Request) {
|
||||
http2.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
@@ -365,7 +536,6 @@ func (s *Server) DeleteGasLimit(w http.ResponseWriter, r *http.Request) {
|
||||
http2.HandleError(w, "Validator service not ready", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
rawPubkey := mux.Vars(r)["pubkey"]
|
||||
if rawPubkey == "" {
|
||||
http2.HandleError(w, "pubkey is required in URL params", http.StatusBadRequest)
|
||||
|
||||
@@ -834,3 +834,426 @@ func TestServer_DeleteRemoteKeys(t *testing.T) {
|
||||
require.Equal(t, 0, len(expectedKeys))
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pubkey := "0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"
|
||||
byteval, err := hexutil.Decode(pubkey)
|
||||
require.NoError(t, err)
|
||||
|
||||
type want struct {
|
||||
EthAddress string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
cached *eth.FeeRecipientByPubKeyResponse
|
||||
}{
|
||||
{
|
||||
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig defined for pubkey (and ProposerSettings.DefaultConfig.FeeRecipientConfig defined)",
|
||||
args: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig NOT defined for pubkey and ProposerSettings.DefaultConfig.FeeRecipientConfig defined",
|
||||
args: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &mock.Validator{}
|
||||
err := m.SetProposerSettings(ctx, tt.args)
|
||||
require.NoError(t, err)
|
||||
|
||||
vs, err := client.NewValidatorService(ctx, &client.Config{
|
||||
Validator: m,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodGet, 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.ListFeeRecipientByPubkey(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
resp := &GetFeeRecipientByPubkeyResponse{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), resp))
|
||||
assert.Equal(t, tt.want.EthAddress, resp.Data.Ethaddress)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubKey_NoFeeRecipientSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
vs, err := client.NewValidatorService(ctx, &client.Config{
|
||||
Validator: &mock.Validator{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.ListFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusOK, w.Code)
|
||||
require.StringContains(t, "No fee recipient set", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
||||
s := &Server{}
|
||||
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0x00"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.SetFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusOK, w.Code)
|
||||
require.StringContains(t, "Validator service not ready", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0x00"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.SetFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusOK, w.Code)
|
||||
require.StringContains(t, "Invalid pubkey", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_FeeRecipientByPubkey(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
beaconClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
pubkey := "0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"
|
||||
byteval, err := hexutil.Decode(pubkey)
|
||||
require.NoError(t, err)
|
||||
|
||||
type want struct {
|
||||
valEthAddress string
|
||||
defaultEthaddress string
|
||||
}
|
||||
type beaconResp struct {
|
||||
resp *eth.FeeRecipientByPubKeyResponse
|
||||
error error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
wantErr bool
|
||||
beaconReturn *beaconResp
|
||||
}{
|
||||
{
|
||||
name: "ProposerSetting is nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: nil,
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: nil,
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil AND ProposerSetting.Defaultconfig is defined",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is defined for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig not defined for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): nil,
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil for pubkey AND DefaultConfig is not nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): nil,
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
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{})
|
||||
|
||||
// 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)
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SetFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0x00"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.SetFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusAccepted, w.Code)
|
||||
require.StringContains(t, "Invalid pubkey", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_SetFeeRecipientByPubkey_InvalidFeeRecipient(t *testing.T) {
|
||||
pubkey := "0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"
|
||||
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
request := &SetFeeRecipientByPubkeyRequest{
|
||||
Ethaddress: "0x00",
|
||||
}
|
||||
|
||||
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.NotEqual(t, http.StatusAccepted, w.Code)
|
||||
|
||||
require.StringContains(t, "Invalid Ethereum Address", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
pubkey := "0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"
|
||||
byteval, err := hexutil.Decode(pubkey)
|
||||
require.NoError(t, err)
|
||||
type want struct {
|
||||
EthAddress string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Happy Path Test",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9738D5"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Hex(),
|
||||
},
|
||||
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,
|
||||
})
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
||||
s := &Server{}
|
||||
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0x1234567878903438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.DeleteFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusNoContent, w.Code)
|
||||
require.StringContains(t, "Validator service not ready", w.Body.String())
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/eth/v1/validator/{pubkey}/feerecipient"), nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"pubkey": "0x123"})
|
||||
w := httptest.NewRecorder()
|
||||
w.Body = &bytes.Buffer{}
|
||||
s.DeleteFeeRecipientByPubkey(w, req)
|
||||
assert.NotEqual(t, http.StatusNoContent, w.Code)
|
||||
|
||||
require.StringContains(t, "pubkey is invalid", w.Body.String())
|
||||
}
|
||||
|
||||
@@ -227,6 +227,9 @@ func (s *Server) InitializeRoutes() error {
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/gas_limit", s.GetGasLimit).Methods(http.MethodGet)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/gas_limit", s.SetGasLimit).Methods(http.MethodPost)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/gas_limit", s.DeleteGasLimit).Methods(http.MethodDelete)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/feerecipient", s.ListFeeRecipientByPubkey).Methods(http.MethodGet)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/feerecipient", s.SetFeeRecipientByPubkey).Methods(http.MethodPost)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/feerecipient", s.DeleteFeeRecipientByPubkey).Methods(http.MethodDelete)
|
||||
s.router.HandleFunc("/eth/v1/validator/{pubkey}/voluntary_exit", s.SetVoluntaryExit).Methods(http.MethodPost)
|
||||
// ...
|
||||
log.Info("Initialized REST API routes")
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestServer_InitializeRoutes(t *testing.T) {
|
||||
wantRouteList := map[string][]string{
|
||||
"/eth/v1/remotekeys": {http.MethodGet, http.MethodPost, http.MethodDelete},
|
||||
"/eth/v1/validator/{pubkey}/gas_limit": {http.MethodGet, http.MethodPost, http.MethodDelete},
|
||||
"/eth/v1/validator/{pubkey}/feerecipient": {http.MethodGet, http.MethodPost, http.MethodDelete},
|
||||
"/eth/v1/validator/{pubkey}/voluntary_exit": {http.MethodPost},
|
||||
}
|
||||
gotRouteList := make(map[string][]string)
|
||||
|
||||
@@ -6,21 +6,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
validatorServiceConfig "github.com/prysmaticlabs/prysm/v4/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
ethpbservice "github.com/prysmaticlabs/prysm/v4/proto/eth/service"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/derived"
|
||||
slashingprotection "github.com/prysmaticlabs/prysm/v4/validator/slashing-protection-history"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/slashing-protection-history/format"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
@@ -260,172 +254,3 @@ func (s *Server) slashingProtectionHistoryForDeletedKeys(
|
||||
}
|
||||
return slashingprotection.ExportStandardProtectionJSON(ctx, s.valDB, filteredKeys...)
|
||||
}
|
||||
|
||||
// ListFeeRecipientByPubkey returns the public key to eth address mapping object to the end user.
|
||||
func (s *Server) ListFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.PubkeyRequest) (*ethpbservice.GetFeeRecipientByPubkeyResponse, error) {
|
||||
if s.validatorService == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Validator service not ready")
|
||||
}
|
||||
|
||||
validatorKey := req.Pubkey
|
||||
if err := validatePublicKey(validatorKey); err != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, err.Error())
|
||||
}
|
||||
|
||||
finalResp := ðpbservice.GetFeeRecipientByPubkeyResponse{
|
||||
Data: ðpbservice.GetFeeRecipientByPubkeyResponse_FeeRecipient{
|
||||
Pubkey: validatorKey,
|
||||
},
|
||||
}
|
||||
|
||||
proposerSettings := s.validatorService.ProposerSettings()
|
||||
|
||||
// If fee recipient is defined for this specific pubkey in proposer configuration, use it
|
||||
if proposerSettings != nil && proposerSettings.ProposeConfig != nil {
|
||||
proposerOption, found := proposerSettings.ProposeConfig[bytesutil.ToBytes48(validatorKey)]
|
||||
|
||||
if found && proposerOption.FeeRecipientConfig != nil {
|
||||
finalResp.Data.Ethaddress = proposerOption.FeeRecipientConfig.FeeRecipient.Bytes()
|
||||
return finalResp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If fee recipient is defined in default configuration, use it
|
||||
if proposerSettings != nil && proposerSettings.DefaultConfig != nil && proposerSettings.DefaultConfig.FeeRecipientConfig != nil {
|
||||
finalResp.Data.Ethaddress = proposerSettings.DefaultConfig.FeeRecipientConfig.FeeRecipient.Bytes()
|
||||
return finalResp, nil
|
||||
}
|
||||
|
||||
// Else, use the one defined in beacon node TODO: remove this with db removal
|
||||
resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, ð.FeeRecipientByPubKeyRequest{
|
||||
PublicKey: validatorKey,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Failed to retrieve default fee recipient from beacon node")
|
||||
}
|
||||
|
||||
if resp != nil && len(resp.FeeRecipient) != 0 {
|
||||
finalResp.Data.Ethaddress = resp.FeeRecipient
|
||||
return finalResp, nil
|
||||
}
|
||||
|
||||
return nil, status.Error(codes.InvalidArgument, "No fee recipient set")
|
||||
}
|
||||
|
||||
// SetFeeRecipientByPubkey updates the eth address mapped to the public key.
|
||||
func (s *Server) SetFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.SetFeeRecipientByPubkeyRequest) (*empty.Empty, error) {
|
||||
if s.validatorService == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Validator service not ready")
|
||||
}
|
||||
|
||||
validatorKey := req.Pubkey
|
||||
feeRecipient := common.BytesToAddress(req.Ethaddress)
|
||||
|
||||
if err := validatePublicKey(validatorKey); err != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, err.Error())
|
||||
}
|
||||
|
||||
encoded := hexutil.Encode(req.Ethaddress)
|
||||
|
||||
if !common.IsHexAddress(encoded) {
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument, "Fee recipient is not a valid Ethereum address")
|
||||
}
|
||||
settings := s.validatorService.ProposerSettings()
|
||||
switch {
|
||||
case settings == nil:
|
||||
settings = &validatorServiceConfig.ProposerSettings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
|
||||
bytesutil.ToBytes48(validatorKey): {
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: nil,
|
||||
},
|
||||
},
|
||||
DefaultConfig: nil,
|
||||
}
|
||||
case settings.ProposeConfig == nil:
|
||||
var builderConfig *validatorServiceConfig.BuilderConfig
|
||||
if settings.DefaultConfig != nil && settings.DefaultConfig.BuilderConfig != nil {
|
||||
builderConfig = settings.DefaultConfig.BuilderConfig.Clone()
|
||||
}
|
||||
settings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
|
||||
bytesutil.ToBytes48(validatorKey): {
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: builderConfig,
|
||||
},
|
||||
}
|
||||
default:
|
||||
proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)]
|
||||
if found && proposerOption != nil {
|
||||
proposerOption.FeeRecipientConfig = &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
}
|
||||
} else {
|
||||
var builderConfig = &validatorServiceConfig.BuilderConfig{}
|
||||
if settings.DefaultConfig != nil && settings.DefaultConfig.BuilderConfig != nil {
|
||||
builderConfig = settings.DefaultConfig.BuilderConfig.Clone()
|
||||
}
|
||||
settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)] = &validatorServiceConfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{
|
||||
FeeRecipient: feeRecipient,
|
||||
},
|
||||
BuilderConfig: builderConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
// save the settings
|
||||
if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil {
|
||||
return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set proposer settings: %v", err)
|
||||
}
|
||||
// override the 200 success with 202 according to the specs
|
||||
if err := grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "202")); err != nil {
|
||||
return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set custom success code header: %v", err)
|
||||
}
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
// DeleteFeeRecipientByPubkey updates the eth address mapped to the public key to the default fee recipient listed
|
||||
func (s *Server) DeleteFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.PubkeyRequest) (*empty.Empty, error) {
|
||||
if s.validatorService == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Validator service not ready")
|
||||
}
|
||||
|
||||
validatorKey := req.Pubkey
|
||||
|
||||
if err := validatePublicKey(validatorKey); err != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, err.Error())
|
||||
}
|
||||
|
||||
settings := s.validatorService.ProposerSettings()
|
||||
|
||||
if settings != nil && settings.ProposeConfig != nil {
|
||||
proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)]
|
||||
if found {
|
||||
proposerOption.FeeRecipientConfig = nil
|
||||
}
|
||||
}
|
||||
|
||||
// save the settings
|
||||
if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil {
|
||||
return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set proposer settings: %v", err)
|
||||
}
|
||||
|
||||
// override the 200 success with 204 according to the specs
|
||||
if err := grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "204")); err != nil {
|
||||
return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set custom success code header: %v", err)
|
||||
}
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func validatePublicKey(pubkey []byte) error {
|
||||
if len(pubkey) != fieldparams.BLSPubkeyLength {
|
||||
return status.Errorf(
|
||||
codes.InvalidArgument, "Provided public key in path is not byte length %d and not a valid bls public key", fieldparams.BLSPubkeyLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,40 +4,29 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/google/uuid"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v4/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
ethpbservice "github.com/prysmaticlabs/prysm/v4/proto/eth/service"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
validatormock "github.com/prysmaticlabs/prysm/v4/testing/validator-mock"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/iface"
|
||||
mock "github.com/prysmaticlabs/prysm/v4/validator/accounts/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/db/kv"
|
||||
dbtest "github.com/prysmaticlabs/prysm/v4/validator/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/derived"
|
||||
remoteweb3signer "github.com/prysmaticlabs/prysm/v4/validator/keymanager/remote-web3signer"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/slashing-protection-history/format"
|
||||
mocks "github.com/prysmaticlabs/prysm/v4/validator/testing"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestServer_ListKeystores(t *testing.T) {
|
||||
@@ -538,452 +527,3 @@ func createRandomKeystore(t testing.TB, password string) *keymanager.Keystore {
|
||||
Description: encryptor.Name(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
|
||||
type want struct {
|
||||
EthAddress string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
cached *eth.FeeRecipientByPubKeyResponse
|
||||
}{
|
||||
{
|
||||
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig defined for pubkey (and ProposerSettings.DefaultConfig.FeeRecipientConfig defined)",
|
||||
args: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig NOT defined for pubkey and ProposerSettings.DefaultConfig.FeeRecipientConfig defined",
|
||||
args: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSettings is nil and beacon node response is correct",
|
||||
args: nil,
|
||||
want: &want{
|
||||
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
cached: ð.FeeRecipientByPubKeyResponse{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Bytes(),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
m := &mock.Validator{}
|
||||
err := m.SetProposerSettings(ctx, tt.args)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.args == nil {
|
||||
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(tt.cached, nil)
|
||||
}
|
||||
|
||||
vs, err := client.NewValidatorService(ctx, &client.Config{
|
||||
Validator: m,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
beaconNodeValidatorClient: mockValidatorClient,
|
||||
}
|
||||
|
||||
got, err := s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.want.EthAddress, common.BytesToAddress(got.Data.Ethaddress).Hex())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubKey_BeaconNodeError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(nil, errors.New("custom error"))
|
||||
|
||||
vs, err := client.NewValidatorService(ctx, &client.Config{
|
||||
Validator: &mock.Validator{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
beaconNodeValidatorClient: mockValidatorClient,
|
||||
}
|
||||
|
||||
_, err = s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
||||
require.ErrorContains(t, "Failed to retrieve default fee recipient from beacon node", err)
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubKey_NoFeeRecipientSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(nil, nil)
|
||||
|
||||
vs, err := client.NewValidatorService(ctx, &client.Config{
|
||||
Validator: &mock.Validator{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
beaconNodeValidatorClient: mockValidatorClient,
|
||||
}
|
||||
|
||||
_, err = s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
||||
require.ErrorContains(t, "No fee recipient set", err)
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
|
||||
s := &Server{}
|
||||
|
||||
_, err := s.ListFeeRecipientByPubkey(ctx, nil)
|
||||
require.ErrorContains(t, "Validator service not ready", err)
|
||||
}
|
||||
|
||||
func TestServer_ListFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := ðpbservice.PubkeyRequest{
|
||||
Pubkey: []byte{},
|
||||
}
|
||||
|
||||
_, err := s.ListFeeRecipientByPubkey(ctx, req)
|
||||
require.ErrorContains(t, "not a valid bls public key", err)
|
||||
}
|
||||
|
||||
func TestServer_FeeRecipientByPubkey(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
beaconClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
|
||||
type want struct {
|
||||
valEthAddress string
|
||||
defaultEthaddress string
|
||||
}
|
||||
type beaconResp struct {
|
||||
resp *eth.FeeRecipientByPubKeyResponse
|
||||
error error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
wantErr bool
|
||||
beaconReturn *beaconResp
|
||||
}{
|
||||
{
|
||||
name: "ProposerSetting is nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: nil,
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: nil,
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil AND ProposerSetting.Defaultconfig is defined",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is defined for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig not defined for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil for pubkey",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): nil,
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProposerSetting.ProposeConfig is nil for pubkey AND DefaultConfig is not nil",
|
||||
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): nil,
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
||||
},
|
||||
want: &want{
|
||||
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
||||
},
|
||||
wantErr: false,
|
||||
beaconReturn: &beaconResp{
|
||||
resp: nil,
|
||||
error: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
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{})
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
_, err = s.SetFeeRecipientByPubkey(ctx, ðpbservice.SetFeeRecipientByPubkeyRequest{Pubkey: byteval, Ethaddress: common.HexToAddress(tt.args).Bytes()})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.want.valEthAddress, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig.FeeRecipient.Hex())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SetFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
|
||||
s := &Server{}
|
||||
|
||||
_, err := s.SetFeeRecipientByPubkey(ctx, nil)
|
||||
require.ErrorContains(t, "Validator service not ready", err)
|
||||
}
|
||||
|
||||
func TestServer_SetFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := ðpbservice.SetFeeRecipientByPubkeyRequest{
|
||||
Pubkey: []byte{},
|
||||
}
|
||||
|
||||
_, err := s.SetFeeRecipientByPubkey(ctx, req)
|
||||
require.ErrorContains(t, "not a valid bls public key", err)
|
||||
}
|
||||
|
||||
func TestServer_SetGasLimit_InvalidFeeRecipient(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := ðpbservice.SetFeeRecipientByPubkeyRequest{
|
||||
Pubkey: byteval,
|
||||
}
|
||||
|
||||
_, err = s.SetFeeRecipientByPubkey(ctx, req)
|
||||
require.ErrorContains(t, "Fee recipient is not a valid Ethereum address", err)
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
||||
require.NoError(t, err)
|
||||
type want struct {
|
||||
EthAddress string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
want *want
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Happy Path Test",
|
||||
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
||||
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
||||
bytesutil.ToBytes48(byteval): {
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9738D5"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &want{
|
||||
EthAddress: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Hex(),
|
||||
},
|
||||
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,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s := &Server{
|
||||
validatorService: vs,
|
||||
valDB: validatorDB,
|
||||
}
|
||||
_, err = s.DeleteFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, true, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig == nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
|
||||
s := &Server{}
|
||||
|
||||
_, err := s.DeleteFeeRecipientByPubkey(ctx, nil)
|
||||
require.ErrorContains(t, "Validator service not ready", err)
|
||||
}
|
||||
|
||||
func TestServer_DeleteFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
||||
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
||||
s := &Server{
|
||||
validatorService: &client.ValidatorService{},
|
||||
}
|
||||
|
||||
req := ðpbservice.PubkeyRequest{
|
||||
Pubkey: []byte{},
|
||||
}
|
||||
|
||||
_, err := s.DeleteFeeRecipientByPubkey(ctx, req)
|
||||
require.ErrorContains(t, "not a valid bls public key", err)
|
||||
}
|
||||
|
||||
@@ -44,3 +44,17 @@ type DeleteRemoteKeysRequest struct {
|
||||
type RemoteKeysResponse struct {
|
||||
Data []*keymanager.KeyStatus `json:"data"`
|
||||
}
|
||||
|
||||
// Fee Recipient keymanager api
|
||||
type FeeRecipient struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
Ethaddress string `json:"ethaddress"`
|
||||
}
|
||||
|
||||
type GetFeeRecipientByPubkeyResponse struct {
|
||||
Data *FeeRecipient `json:"data"`
|
||||
}
|
||||
|
||||
type SetFeeRecipientByPubkeyRequest struct {
|
||||
Ethaddress string `json:"ethaddress"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user