From 315b1db1d6a8ddb2f15e6beeff8a95d1ed022042 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Wed, 26 Apr 2023 10:21:31 +0200 Subject: [PATCH 1/8] registerValidator with only fast decoding --- services/api/service.go | 96 +++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/services/api/service.go b/services/api/service.go index aa8e444e..c28d3d9e 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -708,23 +708,74 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re } req.Body.Close() - parseRegistration := func(value []byte) (pkHex boostTypes.PubkeyHex, timestampInt int64, err error) { - pubkey, err := jsonparser.GetUnsafeString(value, "message", "pubkey") + parseRegistration := func(value []byte) (reg *boostTypes.SignedValidatorRegistration, err error) { + // Pubkey + _pubkey, err := jsonparser.GetUnsafeString(value, "message", "pubkey") if err != nil { - return pkHex, timestampInt, fmt.Errorf("registration message error (pubkey): %w", err) + return nil, fmt.Errorf("registration message error (pubkey): %w", err) } - timestamp, err := jsonparser.GetUnsafeString(value, "message", "timestamp") + pubkey, err := boostTypes.HexToPubkey(_pubkey) if err != nil { - return pkHex, timestampInt, fmt.Errorf("registration message error (timestamp): %w", err) + return nil, fmt.Errorf("registration message error (pubkey): %w", err) } - timestampInt, err = strconv.ParseInt(timestamp, 10, 64) + // Timestamp + _timestamp, err := jsonparser.GetUnsafeString(value, "message", "timestamp") if err != nil { - return pkHex, timestampInt, fmt.Errorf("invalid timestamp: %w", err) + return nil, fmt.Errorf("registration message error (timestamp): %w", err) } - return boostTypes.PubkeyHex(pubkey), timestampInt, nil + timestamp, err := strconv.ParseUint(_timestamp, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid timestamp: %w", err) + } + + // GasLimit + _gasLimit, err := jsonparser.GetUnsafeString(value, "message", "gas_limit") + if err != nil { + return nil, fmt.Errorf("registration message error (gasLimit): %w", err) + } + + gasLimit, err := strconv.ParseUint(_gasLimit, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid gasLimit: %w", err) + } + + // FeeRecipient + _feeRecipient, err := jsonparser.GetUnsafeString(value, "message", "fee_recipient") + if err != nil { + return nil, fmt.Errorf("registration message error (fee_recipient): %w", err) + } + + feeRecipient, err := boostTypes.HexToAddress(_feeRecipient) + if err != nil { + return nil, fmt.Errorf("registration message error (fee_recipient): %w", err) + } + + // Signature + _signature, err := jsonparser.GetUnsafeString(value, "signature") + if err != nil { + return nil, fmt.Errorf("registration message error (signature): %w", err) + } + + signature, err := boostTypes.HexToSignature(_signature) + if err != nil { + return nil, fmt.Errorf("registration message error (signature): %w", err) + } + + // Construct and return full registration object + reg = &boostTypes.SignedValidatorRegistration{ + Message: &boostTypes.RegisterValidatorRequestMessage{ + FeeRecipient: feeRecipient, + GasLimit: gasLimit, + Timestamp: timestamp, + Pubkey: pubkey, + }, + Signature: signature, + } + + return reg, nil } // Iterate over the registrations @@ -736,17 +787,18 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re numRegProcessed += 1 // Extract immediately necessary registration fields - pkHex, timestampInt, err := parseRegistration(value) + signedValidatorRegistration, err := parseRegistration(value) if err != nil { respondError(http.StatusBadRequest, err.Error()) return } // Add validator pubkey to logs - regLog := api.log.WithField("pubkey", pkHex.String()) + pkHex := signedValidatorRegistration.Message.Pubkey.PubkeyHex() + regLog := api.log.WithField("pubkey", pkHex) // Ensure registration is not too far in the future - registrationTime := time.Unix(timestampInt, 0) + registrationTime := time.Unix(int64(signedValidatorRegistration.Message.Timestamp), 0) if registrationTime.After(registrationTimeUpperBound) { respondError(http.StatusBadRequest, "timestamp too far in the future") return @@ -759,7 +811,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re return } - // Track active validators here + // Keep track of active validators numRegActive += 1 select { case api.activeValidatorC <- pkHex: @@ -771,34 +823,24 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re prevTimestamp, err := api.redis.GetValidatorRegistrationTimestamp(pkHex) if err != nil { regLog.WithError(err).Error("error getting last registration timestamp") - } else if prevTimestamp >= uint64(timestampInt) { + } else if prevTimestamp >= signedValidatorRegistration.Message.Timestamp { // abort if the current registration timestamp is older or equal to the last known one return } - // Now we have a new registration to process - numRegNew += 1 - - // JSON-decode the registration now (needed for signature verification) - signedValidatorRegistration := new(boostTypes.SignedValidatorRegistration) - err = json.Unmarshal(value, signedValidatorRegistration) - if err != nil { - regLog.WithError(err).Error("error unmarshalling signed validator registration") - respondError(http.StatusBadRequest, fmt.Sprintf("error unmarshalling signed validator registration: %s", err.Error())) - return - } - // Verify the signature ok, err := boostTypes.VerifySignature(signedValidatorRegistration.Message, api.opts.EthNetDetails.DomainBuilder, signedValidatorRegistration.Message.Pubkey[:], signedValidatorRegistration.Signature[:]) if err != nil { - regLog.WithError(err).Error("error verifying registerValidator signature") respondError(http.StatusBadRequest, fmt.Sprintf("error verifying registerValidator signature: %s", err.Error())) return } else if !ok { - api.RespondError(w, http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) + respondError(http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) return } + // Now we have a new registration to process + numRegNew += 1 + // Save to database select { case api.validatorRegC <- *signedValidatorRegistration: From f90a2307aa91a49902bfa5438654d79bfde60953 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Wed, 26 Apr 2023 13:15:22 +0200 Subject: [PATCH 2/8] more logging on invalid validator sig --- services/api/service.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/api/service.go b/services/api/service.go index c28d3d9e..f27e055f 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -678,6 +678,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re "method": "registerValidator", "ua": ua, "mevBoostV": common.GetMevBoostVersionFromUserAgent(ua), + "headSlot": api.headSlot.Load(), }) start := time.Now().UTC() @@ -834,6 +835,12 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re respondError(http.StatusBadRequest, fmt.Sprintf("error verifying registerValidator signature: %s", err.Error())) return } else if !ok { + regLog.WithFields(logrus.Fields{ + "signature": signedValidatorRegistration.Signature.String(), + "feeRecipient": signedValidatorRegistration.Message.FeeRecipient.String(), + "gasLimit": signedValidatorRegistration.Message.GasLimit, + "timestamp": signedValidatorRegistration.Message.Timestamp, + }).Info("invalid validator signature") respondError(http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) return } From 08d993088999e1f61818c2b2caa7a8d463235577 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Wed, 26 Apr 2023 15:04:57 +0200 Subject: [PATCH 3/8] validator-registration signature check tool --- .../main_test.go | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 internal/investigations/validator-registration-signature-check/main_test.go diff --git a/internal/investigations/validator-registration-signature-check/main_test.go b/internal/investigations/validator-registration-signature-check/main_test.go new file mode 100644 index 00000000..93ff9ce9 --- /dev/null +++ b/internal/investigations/validator-registration-signature-check/main_test.go @@ -0,0 +1,41 @@ +package main + +import ( + "testing" + + boostTypes "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/mev-boost-relay/common" + "github.com/stretchr/testify/require" +) + +func TestSignature(t *testing.T) { + pubkey := "" + gasLimit := 30000000 + feeRecipient := "" + timestamp := 0 + signature := "" + + _pk, err := boostTypes.HexToPubkey(pubkey) + require.NoError(t, err) + _sig, err := boostTypes.HexToSignature(signature) + require.NoError(t, err) + _feeRecipient, err := boostTypes.HexToAddress(feeRecipient) + require.NoError(t, err) + + payload := boostTypes.SignedValidatorRegistration{ + Message: &boostTypes.RegisterValidatorRequestMessage{ + FeeRecipient: _feeRecipient, + GasLimit: uint64(gasLimit), + Timestamp: uint64(timestamp), + Pubkey: _pk, + }, + Signature: _sig, + } + + mainnetDetails, err := common.NewEthNetworkDetails(common.EthNetworkMainnet) + require.NoError(t, err) + + ok, err := boostTypes.VerifySignature(payload.Message, mainnetDetails.DomainBuilder, payload.Message.Pubkey[:], payload.Signature[:]) + require.NoError(t, err) + require.True(t, ok) +} From 5f5aed2247c9e07e89432f3e07d98acb476115d2 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Wed, 26 Apr 2023 18:21:28 +0200 Subject: [PATCH 4/8] more logging, optional continuation on error --- .../main_test.go | 1 + services/api/service.go | 53 ++++++++++++------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/internal/investigations/validator-registration-signature-check/main_test.go b/internal/investigations/validator-registration-signature-check/main_test.go index 93ff9ce9..f76413a7 100644 --- a/internal/investigations/validator-registration-signature-check/main_test.go +++ b/internal/investigations/validator-registration-signature-check/main_test.go @@ -9,6 +9,7 @@ import ( ) func TestSignature(t *testing.T) { + t.Skip() pubkey := "" gasLimit := 30000000 feeRecipient := "" diff --git a/services/api/service.go b/services/api/service.go index f27e055f..94c7fe27 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -72,6 +72,7 @@ var ( // number of goroutines to save active validator numActiveValidatorProcessors = cli.GetEnvInt("NUM_ACTIVE_VALIDATOR_PROCESSORS", 10) numValidatorRegProcessors = cli.GetEnvInt("NUM_VALIDATOR_REG_PROCESSORS", 10) + regValContinueOnError = os.Getenv("REGISTER_VALIDATOR_CONTINUE_ON_ERROR") == "1" // whether to continue processing validators if one fails timeoutGetPayloadRetryMs = cli.GetEnvInt("GETPAYLOAD_RETRY_TIMEOUT_MS", 100) getPayloadRequestCutoffMs = cli.GetEnvInt("GETPAYLOAD_REQUEST_CUTOFF_MS", 4000) getPayloadResponseDelayMs = cli.GetEnvInt("GETPAYLOAD_RESPONSE_DELAY_MS", 1000) @@ -675,10 +676,11 @@ func (api *RelayAPI) handleRoot(w http.ResponseWriter, req *http.Request) { func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Request) { ua := req.UserAgent() log := api.log.WithFields(logrus.Fields{ - "method": "registerValidator", - "ua": ua, - "mevBoostV": common.GetMevBoostVersionFromUserAgent(ua), - "headSlot": api.headSlot.Load(), + "method": "registerValidator", + "ua": ua, + "mevBoostV": common.GetMevBoostVersionFromUserAgent(ua), + "headSlot": api.headSlot.Load(), + "contentLength": req.ContentLength, }) start := time.Now().UTC() @@ -690,14 +692,24 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re numRegNew := 0 processingStoppedByError := false - respondError := func(code int, msg string) { + // Setup error handling + handleError := func(_log *logrus.Entry, code int, msg string) { processingStoppedByError = true - log.Warnf("error: %s", msg) + _log.Warnf("error: %s", msg) api.RespondError(w, code, msg) } + if regValContinueOnError { + // Overload the handleError function to not return an error, but just log it + handleError = func(_log *logrus.Entry, code int, msg string) { + _log.Warnf("error: %s", msg) + } + } + + // Start processing if req.ContentLength == 0 { - respondError(http.StatusBadRequest, "empty request") + log.Info("empty request") + api.RespondError(w, http.StatusBadRequest, "empty request") return } @@ -786,29 +798,33 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re return } numRegProcessed += 1 + regLog := log.WithFields(logrus.Fields{ + "numRegistrationsSoFar": numRegTotal, + "numRegistrationsProcessed": numRegProcessed, + }) // Extract immediately necessary registration fields signedValidatorRegistration, err := parseRegistration(value) if err != nil { - respondError(http.StatusBadRequest, err.Error()) + handleError(regLog, http.StatusBadRequest, err.Error()) return } // Add validator pubkey to logs pkHex := signedValidatorRegistration.Message.Pubkey.PubkeyHex() - regLog := api.log.WithField("pubkey", pkHex) + regLog = regLog.WithField("pubkey", pkHex) // Ensure registration is not too far in the future registrationTime := time.Unix(int64(signedValidatorRegistration.Message.Timestamp), 0) if registrationTime.After(registrationTimeUpperBound) { - respondError(http.StatusBadRequest, "timestamp too far in the future") + handleError(regLog, http.StatusBadRequest, "timestamp too far in the future") return } // Check if a real validator isKnownValidator := api.datastore.IsKnownValidator(pkHex) if !isKnownValidator { - respondError(http.StatusBadRequest, fmt.Sprintf("not a known validator: %s", pkHex.String())) + handleError(regLog, http.StatusBadRequest, fmt.Sprintf("not a known validator: %s", pkHex.String())) return } @@ -832,7 +848,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re // Verify the signature ok, err := boostTypes.VerifySignature(signedValidatorRegistration.Message, api.opts.EthNetDetails.DomainBuilder, signedValidatorRegistration.Message.Pubkey[:], signedValidatorRegistration.Signature[:]) if err != nil { - respondError(http.StatusBadRequest, fmt.Sprintf("error verifying registerValidator signature: %s", err.Error())) + handleError(regLog, http.StatusBadRequest, fmt.Sprintf("error verifying registerValidator signature: %s", err.Error())) return } else if !ok { regLog.WithFields(logrus.Fields{ @@ -841,7 +857,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re "gasLimit": signedValidatorRegistration.Message.GasLimit, "timestamp": signedValidatorRegistration.Message.Timestamp, }).Info("invalid validator signature") - respondError(http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) + handleError(regLog, http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) return } @@ -856,11 +872,6 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re } }) - if err != nil { - respondError(http.StatusBadRequest, "error in traversing json") - return - } - log = log.WithFields(logrus.Fields{ "timeNeededSec": time.Since(start).Seconds(), "timeNeededMs": time.Since(start).Milliseconds(), @@ -870,6 +881,12 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re "numRegistrationsNew": numRegNew, "processingStoppedByError": processingStoppedByError, }) + + if err != nil { + handleError(log, http.StatusBadRequest, "error in traversing json") + return + } + log.Info("validator registrations call processed") w.WriteHeader(http.StatusOK) } From 55bf615f40ddc2ef83051e819b144c4efefe4a61 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Wed, 26 Apr 2023 23:42:49 +0200 Subject: [PATCH 5/8] test cleanup --- .../main_test.go | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/investigations/validator-registration-signature-check/main_test.go b/internal/investigations/validator-registration-signature-check/main_test.go index f76413a7..2da5b37d 100644 --- a/internal/investigations/validator-registration-signature-check/main_test.go +++ b/internal/investigations/validator-registration-signature-check/main_test.go @@ -8,31 +8,33 @@ import ( "github.com/stretchr/testify/require" ) -func TestSignature(t *testing.T) { +// TestValidatorRegistrationSignature can be used to validate the signature of an arbitrary validator registration +func TestValidatorRegistrationSignature(t *testing.T) { t.Skip() + + // Fill in validator registration details pubkey := "" gasLimit := 30000000 feeRecipient := "" timestamp := 0 signature := "" - _pk, err := boostTypes.HexToPubkey(pubkey) - require.NoError(t, err) - _sig, err := boostTypes.HexToSignature(signature) - require.NoError(t, err) - _feeRecipient, err := boostTypes.HexToAddress(feeRecipient) - require.NoError(t, err) - + // Constructing the object payload := boostTypes.SignedValidatorRegistration{ Message: &boostTypes.RegisterValidatorRequestMessage{ - FeeRecipient: _feeRecipient, - GasLimit: uint64(gasLimit), - Timestamp: uint64(timestamp), - Pubkey: _pk, + GasLimit: uint64(gasLimit), + Timestamp: uint64(timestamp), }, - Signature: _sig, } + var err error + payload.Message.Pubkey, err = boostTypes.HexToPubkey(pubkey) + require.NoError(t, err) + payload.Signature, err = boostTypes.HexToSignature(signature) + require.NoError(t, err) + payload.Message.FeeRecipient, err = boostTypes.HexToAddress(feeRecipient) + require.NoError(t, err) + mainnetDetails, err := common.NewEthNetworkDetails(common.EthNetworkMainnet) require.NoError(t, err) From 6036e555ca728bc8c86b274e360363e8147ff83f Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Thu, 27 Apr 2023 16:58:48 +0200 Subject: [PATCH 6/8] cleanup --- services/api/service.go | 61 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/services/api/service.go b/services/api/service.go index 94c7fe27..e8323234 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -72,19 +72,22 @@ var ( // number of goroutines to save active validator numActiveValidatorProcessors = cli.GetEnvInt("NUM_ACTIVE_VALIDATOR_PROCESSORS", 10) numValidatorRegProcessors = cli.GetEnvInt("NUM_VALIDATOR_REG_PROCESSORS", 10) - regValContinueOnError = os.Getenv("REGISTER_VALIDATOR_CONTINUE_ON_ERROR") == "1" // whether to continue processing validators if one fails - timeoutGetPayloadRetryMs = cli.GetEnvInt("GETPAYLOAD_RETRY_TIMEOUT_MS", 100) - getPayloadRequestCutoffMs = cli.GetEnvInt("GETPAYLOAD_REQUEST_CUTOFF_MS", 4000) - getPayloadResponseDelayMs = cli.GetEnvInt("GETPAYLOAD_RESPONSE_DELAY_MS", 1000) + // various timings + timeoutGetPayloadRetryMs = cli.GetEnvInt("GETPAYLOAD_RETRY_TIMEOUT_MS", 100) + getPayloadRequestCutoffMs = cli.GetEnvInt("GETPAYLOAD_REQUEST_CUTOFF_MS", 4000) + getPayloadResponseDelayMs = cli.GetEnvInt("GETPAYLOAD_RESPONSE_DELAY_MS", 1000) + + // api settings apiReadTimeoutMs = cli.GetEnvInt("API_TIMEOUT_READ_MS", 1500) apiReadHeaderTimeoutMs = cli.GetEnvInt("API_TIMEOUT_READHEADER_MS", 600) apiWriteTimeoutMs = cli.GetEnvInt("API_TIMEOUT_WRITE_MS", 10000) apiIdleTimeoutMs = cli.GetEnvInt("API_TIMEOUT_IDLE_MS", 3000) apiMaxHeaderBytes = cli.GetEnvInt("API_MAX_HEADER_BYTES", 60000) + // user-agents which shouldn't receive bids apiNoHeaderUserAgents = common.GetEnvStrSlice("NO_HEADER_USERAGENTS", []string{ - "mev-boost/v1.5.0 Go-http-client/1.1", + "mev-boost/v1.5.0 Go-http-client/1.1", // Prysm v4.0.1 (Shapella signing issue) }) ) @@ -163,6 +166,7 @@ type RelayAPI struct { ffDisablePayloadDBStorage bool // disable storing the execution payloads in the database ffLogInvalidSignaturePayload bool // log payload if getPayload signature validation fails ffEnableCancellations bool // whether to enable block builder cancellations + ffRegValContinueOnInvalidSig bool // whether to continue processing further validators if one fails payloadAttributes map[string]payloadAttributesHelper // key:parentBlockHash payloadAttributesLock sync.RWMutex @@ -259,6 +263,11 @@ func NewRelayAPI(opts RelayAPIOpts) (api *RelayAPI, err error) { api.ffEnableCancellations = true } + if os.Getenv("REGISTER_VALIDATOR_CONTINUE_ON_INVALID_SIG") == "1" { + api.log.Warn("env: REGISTER_VALIDATOR_CONTINUE_ON_INVALID_SIG - validator registration will continue processing even if one validator has an invalid signature") + api.ffRegValContinueOnInvalidSig = true + } + return api, nil } @@ -699,13 +708,6 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re api.RespondError(w, code, msg) } - if regValContinueOnError { - // Overload the handleError function to not return an error, but just log it - handleError = func(_log *logrus.Entry, code int, msg string) { - _log.Warnf("error: %s", msg) - } - } - // Start processing if req.ContentLength == 0 { log.Info("empty request") @@ -812,10 +814,22 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re // Add validator pubkey to logs pkHex := signedValidatorRegistration.Message.Pubkey.PubkeyHex() - regLog = regLog.WithField("pubkey", pkHex) + regLog = regLog.WithFields(logrus.Fields{ + "pubkey": pkHex, + "signature": signedValidatorRegistration.Signature.String(), + "feeRecipient": signedValidatorRegistration.Message.FeeRecipient.String(), + "gasLimit": signedValidatorRegistration.Message.GasLimit, + "timestamp": signedValidatorRegistration.Message.Timestamp, + }) - // Ensure registration is not too far in the future - registrationTime := time.Unix(int64(signedValidatorRegistration.Message.Timestamp), 0) + // Ensure a valid timestamp (not too early, and not too far in the future) + registrationTimestamp := int64(signedValidatorRegistration.Message.Timestamp) + if registrationTimestamp < int64(api.genesisInfo.Data.GenesisTime) { + handleError(regLog, http.StatusBadRequest, "timestamp too early") + return + } + + registrationTime := time.Unix(registrationTimestamp, 0) if registrationTime.After(registrationTimeUpperBound) { handleError(regLog, http.StatusBadRequest, "timestamp too far in the future") return @@ -848,17 +862,16 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re // Verify the signature ok, err := boostTypes.VerifySignature(signedValidatorRegistration.Message, api.opts.EthNetDetails.DomainBuilder, signedValidatorRegistration.Message.Pubkey[:], signedValidatorRegistration.Signature[:]) if err != nil { - handleError(regLog, http.StatusBadRequest, fmt.Sprintf("error verifying registerValidator signature: %s", err.Error())) + regLog.WithError(err).Error("error verifying registerValidator signature") return } else if !ok { - regLog.WithFields(logrus.Fields{ - "signature": signedValidatorRegistration.Signature.String(), - "feeRecipient": signedValidatorRegistration.Message.FeeRecipient.String(), - "gasLimit": signedValidatorRegistration.Message.GasLimit, - "timestamp": signedValidatorRegistration.Message.Timestamp, - }).Info("invalid validator signature") - handleError(regLog, http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) - return + regLog.Info("invalid validator signature") + if api.ffRegValContinueOnInvalidSig { + return + } else { + handleError(regLog, http.StatusBadRequest, fmt.Sprintf("failed to verify validator signature for %s", signedValidatorRegistration.Message.Pubkey.String())) + return + } } // Now we have a new registration to process From 4ca4773999960a4937056d2ff823f2d612198afc Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Fri, 28 Apr 2023 10:17:15 +0200 Subject: [PATCH 7/8] fix time.Unix overflow --- services/api/service.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/services/api/service.go b/services/api/service.go index e8323234..1ac7e523 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -693,7 +693,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re }) start := time.Now().UTC() - registrationTimeUpperBound := start.Add(10 * time.Second) + registrationTimestampUpperBound := start.Unix() + 10 // 10 seconds from now numRegTotal := 0 numRegProcessed := 0 @@ -827,10 +827,7 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re if registrationTimestamp < int64(api.genesisInfo.Data.GenesisTime) { handleError(regLog, http.StatusBadRequest, "timestamp too early") return - } - - registrationTime := time.Unix(registrationTimestamp, 0) - if registrationTime.After(registrationTimeUpperBound) { + } else if registrationTimestamp > registrationTimestampUpperBound { handleError(regLog, http.StatusBadRequest, "timestamp too far in the future") return } From bd4c2e16c24a5bf0e9a81014c9db12117d0bc28a Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Fri, 28 Apr 2023 12:37:11 +0200 Subject: [PATCH 8/8] fix tests --- common/test_utils.go | 9 ++-- .../main.go | 53 +++++++++++++++++++ .../main_test.go | 10 ++-- services/api/service_test.go | 15 ++++-- 4 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 internal/investigations/validator-registration-signature-check/main.go diff --git a/common/test_utils.go b/common/test_utils.go index 74f194b9..64253573 100644 --- a/common/test_utils.go +++ b/common/test_utils.go @@ -41,12 +41,11 @@ func _HexToSignature(s string) (ret types.Signature) { var ValidPayloadRegisterValidator = types.SignedValidatorRegistration{ Message: &types.RegisterValidatorRequestMessage{ FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), - Timestamp: 1234356, - GasLimit: 278234191203, + Timestamp: 1606824043, + GasLimit: 30000000, Pubkey: _HexToPubkey( - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249"), + "0x84e975405f8691ad7118527ee9ee4ed2e4e8bae973f6e29aa9ca9ee4aea83605ae3536d22acc9aa1af0545064eacf82e"), }, - // Signed by 0x4e343a647c5a5c44d76c2c58b63f02cdf3a9a0ec40f102ebc26363b4b1b95033 Signature: _HexToSignature( - "0x8209b5391cd69f392b1f02dbc03bab61f574bb6bb54bf87b59e2a85bdc0756f7db6a71ce1b41b727a1f46ccc77b213bf0df1426177b5b29926b39956114421eaa36ec4602969f6f6370a44de44a6bce6dae2136e5fb594cce2a476354264d1ea"), + "0xaf12df007a0c78abb5575067e5f8b089cfcc6227e4a91db7dd8cf517fe86fb944ead859f0781277d9b78c672e4a18c5d06368b603374673cf2007966cece9540f3a1b3f6f9e1bf421d779c4e8010368e6aac134649c7a009210780d401a778a5"), } diff --git a/internal/investigations/validator-registration-signature-check/main.go b/internal/investigations/validator-registration-signature-check/main.go new file mode 100644 index 00000000..c06555d7 --- /dev/null +++ b/internal/investigations/validator-registration-signature-check/main.go @@ -0,0 +1,53 @@ +package main + +// +// Script to create a signed validator registration +// + +import ( + "fmt" + + "github.com/flashbots/go-boost-utils/bls" + boostTypes "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/mev-boost-relay/common" +) + +var ( + gasLimit = 30000000 + feeRecipient = "0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941" + timestamp = 1606824043 +) + +func Perr(err error) { + if err != nil { + panic(err) + } +} + +func main() { + mainnetDetails, err := common.NewEthNetworkDetails(common.EthNetworkMainnet) + Perr(err) + + sk, pubkey, err := bls.GenerateNewKeypair() + Perr(err) + + pk, err := boostTypes.BlsPublicKeyToPublicKey(pubkey) + Perr(err) + + // Fill in validator registration details + validatorRegistration := boostTypes.RegisterValidatorRequestMessage{ //nolint:exhaustruct + GasLimit: uint64(gasLimit), + Timestamp: uint64(timestamp), + } + + validatorRegistration.Pubkey, err = boostTypes.HexToPubkey(pk.String()) + Perr(err) + validatorRegistration.FeeRecipient, err = boostTypes.HexToAddress(feeRecipient) + Perr(err) + + sig, err := boostTypes.SignMessage(&validatorRegistration, mainnetDetails.DomainBuilder, sk) + Perr(err) + fmt.Println("privkey:", sk.String()) + fmt.Println("pubkey: ", pk.String()) + fmt.Println("sig: ", sig.String()) +} diff --git a/internal/investigations/validator-registration-signature-check/main_test.go b/internal/investigations/validator-registration-signature-check/main_test.go index 2da5b37d..d1528d09 100644 --- a/internal/investigations/validator-registration-signature-check/main_test.go +++ b/internal/investigations/validator-registration-signature-check/main_test.go @@ -10,14 +10,12 @@ import ( // TestValidatorRegistrationSignature can be used to validate the signature of an arbitrary validator registration func TestValidatorRegistrationSignature(t *testing.T) { - t.Skip() - // Fill in validator registration details - pubkey := "" + pubkey := "0x84e975405f8691ad7118527ee9ee4ed2e4e8bae973f6e29aa9ca9ee4aea83605ae3536d22acc9aa1af0545064eacf82e" gasLimit := 30000000 - feeRecipient := "" - timestamp := 0 - signature := "" + feeRecipient := "0xdb65fed33dc262fe09d9a2ba8f80b329ba25f941" + timestamp := 1606824043 + signature := "0xaf12df007a0c78abb5575067e5f8b089cfcc6227e4a91db7dd8cf517fe86fb944ead859f0781277d9b78c672e4a18c5d06368b603374673cf2007966cece9540f3a1b3f6f9e1bf421d779c4e8010368e6aac134649c7a009210780d401a778a5" // Constructing the object payload := boostTypes.SignedValidatorRegistration{ diff --git a/services/api/service_test.go b/services/api/service_test.go index 809bec52..c71c6c1b 100644 --- a/services/api/service_test.go +++ b/services/api/service_test.go @@ -64,6 +64,12 @@ func newTestBackend(t require.TestingT, numBeaconNodes int) *testBackend { relay, err := NewRelayAPI(opts) require.NoError(t, err) + relay.genesisInfo = &beaconclient.GetGenesisResponse{ + Data: beaconclient.GetGenesisResponseData{ + GenesisTime: 1606824023, + }, + } + backend := testBackend{ t: t, relay: relay, @@ -184,14 +190,13 @@ func TestRegisterValidator(t *testing.T) { require.True(t, ok) require.Equal(t, pubkeyHex, pkH) - rr := backend.request(http.MethodPost, path, []types.SignedValidatorRegistration{common.ValidPayloadRegisterValidator}) + payload := []types.SignedValidatorRegistration{common.ValidPayloadRegisterValidator} + rr := backend.request(http.MethodPost, path, payload) require.Equal(t, http.StatusOK, rr.Code) time.Sleep(20 * time.Millisecond) // registrations are processed asynchronously - // req, err := backend.datastore.GetValidatorRegistration(pubkeyHex) - // require.NoError(t, err) - // require.NotNil(t, req) - // require.Equal(t, pubkeyHex, req.Message.Pubkey.PubkeyHex()) + isKnown := backend.datastore.IsKnownValidator(pubkeyHex) + require.True(t, isKnown) }) t.Run("not a known validator", func(t *testing.T) {