From b17921a149c97f77529e9c6b78e1a963c2fd1eab Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 4 Apr 2024 18:56:19 +0200 Subject: [PATCH] server: move mock relay into mock package (#642) * server: move mock relay into mock package * server/mock: fix comment * server/mock: overrideGetHeader properly * server/mock: disallowUnknownFields * happy lint, happy life --- cli/types.go | 10 +-- server/backend.go | 9 --- server/{ => mock}/mock_relay.go | 87 +++++++++++++++----------- server/{ => mock}/mock_relay_test.go | 7 ++- server/mock/mock_types.go | 61 ++++++++++++++++++ server/{ => mock}/mock_types_test.go | 26 ++++---- server/mock_types.go | 61 ------------------ server/params/paths.go | 9 +++ server/service.go | 45 ++++++------- server/service_test.go | 55 ++++++++-------- server/types/U256str.go | 11 ++++ server/{ => types}/errors.go | 2 +- server/{ => types}/relay_entry.go | 10 ++- server/{ => types}/relay_entry_test.go | 2 +- server/utils.go | 12 +--- 15 files changed, 216 insertions(+), 191 deletions(-) delete mode 100644 server/backend.go rename server/{ => mock}/mock_relay.go (74%) rename server/{ => mock}/mock_relay_test.go (63%) create mode 100644 server/mock/mock_types.go rename server/{ => mock}/mock_types_test.go (92%) delete mode 100644 server/mock_types.go create mode 100644 server/params/paths.go create mode 100644 server/types/U256str.go rename server/{ => types}/errors.go (95%) rename server/{ => types}/relay_entry.go (90%) rename server/{ => types}/relay_entry_test.go (99%) diff --git a/cli/types.go b/cli/types.go index 41473700..28c99b39 100644 --- a/cli/types.go +++ b/cli/types.go @@ -5,18 +5,18 @@ import ( "net/url" "strings" - "github.com/flashbots/mev-boost/server" + "github.com/flashbots/mev-boost/server/types" ) var errDuplicateEntry = errors.New("duplicate entry") -type relayList []server.RelayEntry +type relayList []types.RelayEntry func (r *relayList) String() string { - return strings.Join(server.RelayEntriesToStrings(*r), ",") + return strings.Join(types.RelayEntriesToStrings(*r), ",") } -func (r *relayList) Contains(relay server.RelayEntry) bool { +func (r *relayList) Contains(relay types.RelayEntry) bool { for _, entry := range *r { if relay.String() == entry.String() { return true @@ -26,7 +26,7 @@ func (r *relayList) Contains(relay server.RelayEntry) bool { } func (r *relayList) Set(value string) error { - relay, err := server.NewRelayEntry(value) + relay, err := types.NewRelayEntry(value) if err != nil { return err } diff --git a/server/backend.go b/server/backend.go deleted file mode 100644 index afbea341..00000000 --- a/server/backend.go +++ /dev/null @@ -1,9 +0,0 @@ -package server - -const ( - // Router paths - pathStatus = "/eth/v1/builder/status" - pathRegisterValidator = "/eth/v1/builder/validators" - pathGetHeader = "/eth/v1/builder/header/{slot:[0-9]+}/{parent_hash:0x[a-fA-F0-9]+}/{pubkey:0x[a-fA-F0-9]+}" - pathGetPayload = "/eth/v1/builder/blinded_blocks" -) diff --git a/server/mock_relay.go b/server/mock/mock_relay.go similarity index 74% rename from server/mock_relay.go rename to server/mock/mock_relay.go index fe6c6daa..c74d2c87 100644 --- a/server/mock_relay.go +++ b/server/mock/mock_relay.go @@ -1,4 +1,4 @@ -package server +package mock import ( "encoding/json" @@ -22,6 +22,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/flashbots/go-boost-utils/bls" "github.com/flashbots/go-boost-utils/ssz" + "github.com/flashbots/mev-boost/server/params" + "github.com/flashbots/mev-boost/server/types" "github.com/gorilla/mux" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -37,17 +39,17 @@ var ( mockRelayPublicKey, _ = bls.PublicKeyFromSecretKey(mockRelaySecretKey) ) -// mockRelay is used to fake a relay's behavior. +// Relay is used to fake a relay's behavior. // You can override each of its handler by setting the instance's HandlerOverride_METHOD_TO_OVERRIDE to your own // handler. -type mockRelay struct { +type Relay struct { // Used to panic if impossible error happens t *testing.T // KeyPair used to sign messages secretKey *bls.SecretKey publicKey *bls.PublicKey - RelayEntry RelayEntry + RelayEntry types.RelayEntry // Used to count each Request made to the relay, either if it fails or not, for each method mu sync.Mutex @@ -67,11 +69,11 @@ type mockRelay struct { ResponseDelay time.Duration } -// newMockRelay creates a mocked relay which implements the backend.BoostBackend interface +// NewRelay creates a mocked relay which implements the backend.BoostBackend interface // A secret key must be provided to sign default and custom response messages -func newMockRelay(t *testing.T) *mockRelay { +func NewRelay(t *testing.T) *Relay { t.Helper() - relay := &mockRelay{t: t, secretKey: mockRelaySecretKey, publicKey: mockRelayPublicKey, requestCount: make(map[string]int)} + relay := &Relay{t: t, secretKey: mockRelaySecretKey, publicKey: mockRelayPublicKey, requestCount: make(map[string]int)} // Initialize server relay.Server = httptest.NewServer(relay.getRouter()) @@ -80,13 +82,13 @@ func newMockRelay(t *testing.T) *mockRelay { url, err := url.Parse(relay.Server.URL) require.NoError(t, err) urlWithKey := fmt.Sprintf("%s://%s@%s", url.Scheme, hexutil.Encode(bls.PublicKeyToBytes(mockRelayPublicKey)), url.Host) - relay.RelayEntry, err = NewRelayEntry(urlWithKey) + relay.RelayEntry, err = types.NewRelayEntry(urlWithKey) require.NoError(t, err) return relay } // newTestMiddleware creates a middleware which increases the Request counter and creates a fake delay for the response -func (m *mockRelay) newTestMiddleware(next http.Handler) http.Handler { +func (m *Relay) newTestMiddleware(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { // Request counter @@ -106,43 +108,43 @@ func (m *mockRelay) newTestMiddleware(next http.Handler) http.Handler { } // getRouter registers all methods from the backend, apply the test middleware and return the configured router -func (m *mockRelay) getRouter() http.Handler { +func (m *Relay) getRouter() http.Handler { // Create router. r := mux.NewRouter() // Register handlers r.HandleFunc("/", m.handleRoot).Methods(http.MethodGet) - r.HandleFunc(pathStatus, m.handleStatus).Methods(http.MethodGet) - r.HandleFunc(pathRegisterValidator, m.handleRegisterValidator).Methods(http.MethodPost) - r.HandleFunc(pathGetHeader, m.handleGetHeader).Methods(http.MethodGet) - r.HandleFunc(pathGetPayload, m.handleGetPayload).Methods(http.MethodPost) + r.HandleFunc(params.PathStatus, m.handleStatus).Methods(http.MethodGet) + r.HandleFunc(params.PathRegisterValidator, m.handleRegisterValidator).Methods(http.MethodPost) + r.HandleFunc(params.PathGetHeader, m.handleGetHeader).Methods(http.MethodGet) + r.HandleFunc(params.PathGetPayload, m.handleGetPayload).Methods(http.MethodPost) return m.newTestMiddleware(r) } // GetRequestCount returns the number of Request made to a specific URL -func (m *mockRelay) GetRequestCount(path string) int { +func (m *Relay) GetRequestCount(path string) int { m.mu.Lock() defer m.mu.Unlock() return m.requestCount[path] } // By default, handleRoot returns the relay's status -func (m *mockRelay) handleRoot(w http.ResponseWriter, _ *http.Request) { +func (m *Relay) handleRoot(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{}`) } // By default, handleStatus returns the relay's status as http.StatusOK -func (m *mockRelay) handleStatus(w http.ResponseWriter, _ *http.Request) { +func (m *Relay) handleStatus(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{}`) } // By default, handleRegisterValidator returns a default builderApiV1.SignedValidatorRegistration -func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Request) { +func (m *Relay) handleRegisterValidator(w http.ResponseWriter, req *http.Request) { m.mu.Lock() defer m.mu.Unlock() if m.handlerOverrideRegisterValidator != nil { @@ -153,9 +155,11 @@ func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Req } // defaultHandleRegisterValidator returns the default handler for handleRegisterValidator -func (m *mockRelay) defaultHandleRegisterValidator(w http.ResponseWriter, req *http.Request) { +func (m *Relay) defaultHandleRegisterValidator(w http.ResponseWriter, req *http.Request) { payload := []builderApiV1.SignedValidatorRegistration{} - if err := DecodeJSON(req.Body, &payload); err != nil { + decoder := json.NewDecoder(req.Body) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&payload); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -166,18 +170,18 @@ func (m *mockRelay) defaultHandleRegisterValidator(w http.ResponseWriter, req *h // MakeGetHeaderResponse is used to create the default or can be used to create a custom response to the getHeader // method -func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, publicKey string, version spec.DataVersion) *builderSpec.VersionedSignedBuilderBid { +func (m *Relay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, publicKey string, version spec.DataVersion) *builderSpec.VersionedSignedBuilderBid { switch version { case spec.DataVersionCapella: // Fill the payload with custom values. message := &builderApiCapella.BuilderBid{ Header: &capella.ExecutionPayloadHeader{ - BlockHash: _HexToHash(blockHash), - ParentHash: _HexToHash(parentHash), + BlockHash: HexToHash(blockHash), + ParentHash: HexToHash(parentHash), WithdrawalsRoot: phase0.Root{}, }, Value: uint256.NewInt(value), - Pubkey: _HexToPubkey(publicKey), + Pubkey: HexToPubkey(publicKey), } // Sign the message. @@ -194,14 +198,14 @@ func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, p case spec.DataVersionDeneb: message := &builderApiDeneb.BuilderBid{ Header: &deneb.ExecutionPayloadHeader{ - BlockHash: _HexToHash(blockHash), - ParentHash: _HexToHash(parentHash), + BlockHash: HexToHash(blockHash), + ParentHash: HexToHash(parentHash), WithdrawalsRoot: phase0.Root{}, BaseFeePerGas: uint256.NewInt(0), }, BlobKZGCommitments: make([]deneb.KZGCommitment, 0), Value: uint256.NewInt(value), - Pubkey: _HexToPubkey(publicKey), + Pubkey: HexToPubkey(publicKey), } // Sign the message. @@ -222,7 +226,7 @@ func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, p } // handleGetHeader handles incoming requests to server.pathGetHeader -func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) { +func (m *Relay) handleGetHeader(w http.ResponseWriter, req *http.Request) { m.mu.Lock() defer m.mu.Unlock() // Try to override default behavior is custom handler is specified. @@ -234,7 +238,7 @@ func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) { } // defaultHandleGetHeader returns the default handler for handleGetHeader -func (m *mockRelay) defaultHandleGetHeader(w http.ResponseWriter) { +func (m *Relay) defaultHandleGetHeader(w http.ResponseWriter) { // By default, everything will be ok. w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -259,21 +263,21 @@ func (m *mockRelay) defaultHandleGetHeader(w http.ResponseWriter) { // MakeGetPayloadResponse is used to create the default or can be used to create a custom response to the getPayload // method -func (m *mockRelay) MakeGetPayloadResponse(parentHash, blockHash, feeRecipient string, blockNumber uint64, version spec.DataVersion) *builderApi.VersionedSubmitBlindedBlockResponse { +func (m *Relay) MakeGetPayloadResponse(parentHash, blockHash, feeRecipient string, blockNumber uint64, version spec.DataVersion) *builderApi.VersionedSubmitBlindedBlockResponse { return &builderApi.VersionedSubmitBlindedBlockResponse{ Version: version, Capella: &capella.ExecutionPayload{ - ParentHash: _HexToHash(parentHash), - BlockHash: _HexToHash(blockHash), + ParentHash: HexToHash(parentHash), + BlockHash: HexToHash(blockHash), BlockNumber: blockNumber, - FeeRecipient: _HexToAddress(feeRecipient), + FeeRecipient: HexToAddress(feeRecipient), Withdrawals: make([]*capella.Withdrawal, 0), }, } } // handleGetPayload handles incoming requests to server.pathGetPayload -func (m *mockRelay) handleGetPayload(w http.ResponseWriter, req *http.Request) { +func (m *Relay) handleGetPayload(w http.ResponseWriter, req *http.Request) { m.mu.Lock() defer m.mu.Unlock() // Try to override default behavior is custom handler is specified. @@ -281,11 +285,11 @@ func (m *mockRelay) handleGetPayload(w http.ResponseWriter, req *http.Request) { m.handlerOverrideGetPayload(w, req) return } - m.defaultHandleGetPayload(w) + m.DefaultHandleGetPayload(w) } -// defaultHandleGetPayload returns the default handler for handleGetPayload -func (m *mockRelay) defaultHandleGetPayload(w http.ResponseWriter) { +// DefaultHandleGetPayload returns the default handler for handleGetPayload +func (m *Relay) DefaultHandleGetPayload(w http.ResponseWriter) { // By default, everything will be ok. w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -309,9 +313,16 @@ func (m *mockRelay) defaultHandleGetPayload(w http.ResponseWriter) { } } -func (m *mockRelay) overrideHandleRegisterValidator(method func(w http.ResponseWriter, req *http.Request)) { +func (m *Relay) OverrideHandleRegisterValidator(method func(w http.ResponseWriter, req *http.Request)) { m.mu.Lock() defer m.mu.Unlock() m.handlerOverrideRegisterValidator = method } + +func (m *Relay) OverrideHandleGetPayload(method func(w http.ResponseWriter, req *http.Request)) { + m.mu.Lock() + defer m.mu.Unlock() + + m.handlerOverrideGetPayload = method +} diff --git a/server/mock_relay_test.go b/server/mock/mock_relay_test.go similarity index 63% rename from server/mock_relay_test.go rename to server/mock/mock_relay_test.go index fce4bdd7..86fa560f 100644 --- a/server/mock_relay_test.go +++ b/server/mock/mock_relay_test.go @@ -1,4 +1,4 @@ -package server +package mock import ( "bytes" @@ -6,13 +6,14 @@ import ( "net/http/httptest" "testing" + "github.com/flashbots/mev-boost/server/params" "github.com/stretchr/testify/require" ) func Test_mockRelay(t *testing.T) { t.Run("bad payload", func(t *testing.T) { - relay := newMockRelay(t) - req, err := http.NewRequest(http.MethodPost, pathRegisterValidator, bytes.NewReader([]byte("123"))) + relay := NewRelay(t) + req, err := http.NewRequest(http.MethodPost, params.PathRegisterValidator, bytes.NewReader([]byte("123"))) require.NoError(t, err) rr := httptest.NewRecorder() relay.getRouter().ServeHTTP(rr, req) diff --git a/server/mock/mock_types.go b/server/mock/mock_types.go new file mode 100644 index 00000000..117fb218 --- /dev/null +++ b/server/mock/mock_types.go @@ -0,0 +1,61 @@ +package mock + +import ( + "github.com/attestantio/go-eth2-client/spec/bellatrix" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/flashbots/go-boost-utils/utils" + "github.com/sirupsen/logrus" +) + +// TestLog is used to log information in the test methods +var TestLog = logrus.NewEntry(logrus.New()) + +// HexToBytes converts a hexadecimal string to a byte array +func HexToBytes(hex string) []byte { + res, err := hexutil.Decode(hex) + if err != nil { + panic(err) + } + return res +} + +// HexToHash converts a hexadecimal string to an Ethereum hash +func HexToHash(s string) (ret phase0.Hash32) { + ret, err := utils.HexToHash(s) + if err != nil { + TestLog.Error(err, " _HexToHash: ", s) + panic(err) + } + return ret +} + +// HexToAddress converts a hexadecimal string to an Ethereum address +func HexToAddress(s string) (ret bellatrix.ExecutionAddress) { + ret, err := utils.HexToAddress(s) + if err != nil { + TestLog.Error(err, " _HexToAddress: ", s) + panic(err) + } + return ret +} + +// HexToPubkey converts a hexadecimal string to a BLS Public Key +func HexToPubkey(s string) (ret phase0.BLSPubKey) { + ret, err := utils.HexToPubkey(s) + if err != nil { + TestLog.Error(err, " _HexToPubkey: ", s) + panic(err) + } + return +} + +// HexToSignature converts a hexadecimal string to a BLS Signature +func HexToSignature(s string) (ret phase0.BLSSignature) { + ret, err := utils.HexToSignature(s) + if err != nil { + TestLog.Error(err, " _HexToSignature: ", s) + panic(err) + } + return +} diff --git a/server/mock_types_test.go b/server/mock/mock_types_test.go similarity index 92% rename from server/mock_types_test.go rename to server/mock/mock_types_test.go index adc156e5..f47493b9 100644 --- a/server/mock_types_test.go +++ b/server/mock/mock_types_test.go @@ -1,4 +1,4 @@ -package server +package mock import ( "testing" @@ -41,11 +41,11 @@ func TestHexToBytes(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.expectedPanic { require.Panics(t, func() { - _HexToBytes(tt.hex) + HexToBytes(tt.hex) }) } else { require.NotPanics(t, func() { - actualBytes := _HexToBytes(tt.hex) + actualBytes := HexToBytes(tt.hex) require.Equal(t, tt.expectedBytes, actualBytes) }) } @@ -88,11 +88,11 @@ func TestHexToHash(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.expectedPanic { require.Panics(t, func() { - _HexToHash(tt.hex) + HexToHash(tt.hex) }) } else { require.NotPanics(t, func() { - actualHash := _HexToHash(tt.hex) + actualHash := HexToHash(tt.hex) require.Equal(t, *tt.expectedHash, actualHash) }) } @@ -135,11 +135,11 @@ func TestHexToAddress(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.expectedPanic { require.Panics(t, func() { - _HexToAddress(tt.hex) + HexToAddress(tt.hex) }) } else { require.NotPanics(t, func() { - actualAddress := _HexToAddress(tt.hex) + actualAddress := HexToAddress(tt.hex) require.Equal(t, *tt.expectedAddress, actualAddress) }) } @@ -182,11 +182,11 @@ func TestHexToPublicKey(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.expectedPanic { require.Panics(t, func() { - _HexToPubkey(tt.hex) + HexToPubkey(tt.hex) }) } else { require.NotPanics(t, func() { - actualPublicKey := _HexToPubkey(tt.hex) + actualPublicKey := HexToPubkey(tt.hex) require.Equal(t, *tt.expectedPublicKey, actualPublicKey) }) } @@ -204,10 +204,10 @@ func TestHexToSignature(t *testing.T) { message := &builderApiCapella.BuilderBid{ Header: &capella.ExecutionPayloadHeader{ - BlockHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), + BlockHash: HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), }, Value: uint256.NewInt(12345), - Pubkey: _HexToPubkey(publicKey), + Pubkey: HexToPubkey(publicKey), } ssz, err := message.MarshalSSZ() require.NoError(t, err) @@ -252,11 +252,11 @@ func TestHexToSignature(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.expectedPanic { require.Panics(t, func() { - _HexToSignature(tt.hex) + HexToSignature(tt.hex) }) } else { require.NotPanics(t, func() { - actualSignature := _HexToSignature(tt.hex) + actualSignature := HexToSignature(tt.hex) require.Equal(t, *tt.expectedSignature, actualSignature) }) } diff --git a/server/mock_types.go b/server/mock_types.go deleted file mode 100644 index a956b94b..00000000 --- a/server/mock_types.go +++ /dev/null @@ -1,61 +0,0 @@ -package server - -import ( - "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/flashbots/go-boost-utils/utils" - "github.com/sirupsen/logrus" -) - -// testLog is used to log information in the test methods -var testLog = logrus.NewEntry(logrus.New()) - -// _HexToBytes converts a hexadecimal string to a byte array -func _HexToBytes(hex string) []byte { - res, err := hexutil.Decode(hex) - if err != nil { - panic(err) - } - return res -} - -// _HexToHash converts a hexadecimal string to an Ethereum hash -func _HexToHash(s string) (ret phase0.Hash32) { - ret, err := utils.HexToHash(s) - if err != nil { - testLog.Error(err, " _HexToHash: ", s) - panic(err) - } - return ret -} - -// _HexToAddress converts a hexadecimal string to an Ethereum address -func _HexToAddress(s string) (ret bellatrix.ExecutionAddress) { - ret, err := utils.HexToAddress(s) - if err != nil { - testLog.Error(err, " _HexToAddress: ", s) - panic(err) - } - return ret -} - -// _HexToPubkey converts a hexadecimal string to a BLS Public Key -func _HexToPubkey(s string) (ret phase0.BLSPubKey) { - ret, err := utils.HexToPubkey(s) - if err != nil { - testLog.Error(err, " _HexToPubkey: ", s) - panic(err) - } - return -} - -// _HexToSignature converts a hexadecimal string to a BLS Signature -func _HexToSignature(s string) (ret phase0.BLSSignature) { - ret, err := utils.HexToSignature(s) - if err != nil { - testLog.Error(err, " _HexToSignature: ", s) - panic(err) - } - return -} diff --git a/server/params/paths.go b/server/params/paths.go new file mode 100644 index 00000000..4414f088 --- /dev/null +++ b/server/params/paths.go @@ -0,0 +1,9 @@ +package params + +const ( + // Router paths + PathStatus = "/eth/v1/builder/status" + PathRegisterValidator = "/eth/v1/builder/validators" + PathGetHeader = "/eth/v1/builder/header/{slot:[0-9]+}/{parent_hash:0x[a-fA-F0-9]+}/{pubkey:0x[a-fA-F0-9]+}" + PathGetPayload = "/eth/v1/builder/blinded_blocks" +) diff --git a/server/service.go b/server/service.go index 42a68aa8..46e37f94 100644 --- a/server/service.go +++ b/server/service.go @@ -22,10 +22,11 @@ import ( eth2ApiV1Deneb "github.com/attestantio/go-eth2-client/api/v1/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/flashbots/go-boost-utils/ssz" - "github.com/flashbots/go-boost-utils/types" "github.com/flashbots/go-boost-utils/utils" "github.com/flashbots/go-utils/httplogger" "github.com/flashbots/mev-boost/config" + "github.com/flashbots/mev-boost/server/params" + "github.com/flashbots/mev-boost/server/types" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/sirupsen/logrus" @@ -59,7 +60,7 @@ type slotUID struct { type BoostServiceOpts struct { Log *logrus.Entry ListenAddr string - Relays []RelayEntry + Relays []types.RelayEntry RelayMonitors []*url.URL GenesisForkVersionHex string GenesisTime uint64 @@ -75,7 +76,7 @@ type BoostServiceOpts struct { // BoostService - the mev-boost service type BoostService struct { listenAddr string - relays []RelayEntry + relays []types.RelayEntry relayMonitors []*url.URL log *logrus.Entry srv *http.Server @@ -158,10 +159,10 @@ func (m *BoostService) getRouter() http.Handler { r := mux.NewRouter() r.HandleFunc("/", m.handleRoot) - r.HandleFunc(pathStatus, m.handleStatus).Methods(http.MethodGet) - r.HandleFunc(pathRegisterValidator, m.handleRegisterValidator).Methods(http.MethodPost) - r.HandleFunc(pathGetHeader, m.handleGetHeader).Methods(http.MethodGet) - r.HandleFunc(pathGetPayload, m.handleGetPayload).Methods(http.MethodPost) + r.HandleFunc(params.PathStatus, m.handleStatus).Methods(http.MethodGet) + r.HandleFunc(params.PathRegisterValidator, m.handleRegisterValidator).Methods(http.MethodPost) + r.HandleFunc(params.PathGetHeader, m.handleGetHeader).Methods(http.MethodGet) + r.HandleFunc(params.PathGetPayload, m.handleGetPayload).Methods(http.MethodPost) r.Use(mux.CORSMethodMiddleware(r)) loggedRouter := httplogger.LoggingMiddlewareLogrus(m.log, r) @@ -212,7 +213,7 @@ func (m *BoostService) sendValidatorRegistrationsToRelayMonitors(payload []build log := m.log.WithField("method", "sendValidatorRegistrationsToRelayMonitors").WithField("numRegistrations", len(payload)) for _, relayMonitor := range m.relayMonitors { go func(relayMonitor *url.URL) { - url := GetURI(relayMonitor, pathRegisterValidator) + url := types.GetURI(relayMonitor, params.PathRegisterValidator) log = log.WithField("url", url) _, err := SendHTTPRequest(context.Background(), m.httpClientRegVal, http.MethodPost, url, "", nil, payload, nil) if err != nil { @@ -259,8 +260,8 @@ func (m *BoostService) handleRegisterValidator(w http.ResponseWriter, req *http. relayRespCh := make(chan error, len(m.relays)) for _, relay := range m.relays { - go func(relay RelayEntry) { - url := relay.GetURI(pathRegisterValidator) + go func(relay types.RelayEntry) { + url := relay.GetURI(params.PathRegisterValidator) log := log.WithField("url", url) _, err := SendHTTPRequest(context.Background(), m.httpClientRegVal, http.MethodPost, url, ua, nil, payload, nil) @@ -343,15 +344,15 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) } // Prepare relay responses - result := bidResp{} // the final response, containing the highest bid (if any) - relays := make(map[BlockHashHex][]RelayEntry) // relays that sent the bid for a specific blockHash + result := bidResp{} // the final response, containing the highest bid (if any) + relays := make(map[BlockHashHex][]types.RelayEntry) // relays that sent the bid for a specific blockHash // Call the relays var mu sync.Mutex var wg sync.WaitGroup for _, relay := range m.relays { wg.Add(1) - go func(relay RelayEntry) { + go func(relay types.RelayEntry) { defer wg.Done() path := fmt.Sprintf("/eth/v1/builder/header/%s/%s/%s", slot, parentHashHex, pubkey) url := relay.GetURI(path) @@ -478,7 +479,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) "blockNumber": result.bidInfo.blockNumber, "txRoot": result.bidInfo.txRoot.String(), "value": valueEth.Text('f', 18), - "relays": strings.Join(RelayEntriesToStrings(result.relays), ", "), + "relays": strings.Join(types.RelayEntriesToStrings(result.relays), ", "), }).Info("best bid") // Remember the bid, for future logging in case of withholding @@ -552,9 +553,9 @@ func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Re for _, relay := range m.relays { wg.Add(1) - go func(relay RelayEntry) { + go func(relay types.RelayEntry) { defer wg.Done() - url := relay.GetURI(pathGetPayload) + url := relay.GetURI(params.PathGetPayload) log := log.WithField("url", url) log.Debug("calling getPayload") @@ -616,7 +617,7 @@ func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Re // If no payload has been received from relay, log loudly about withholding! if result.Capella == nil || result.Capella.BlockHash == nilHash { - originRelays := RelayEntriesToStrings(originalBid.relays) + originRelays := types.RelayEntriesToStrings(originalBid.relays) log.WithField("relaysWithBid", strings.Join(originRelays, ", ")).Error("no payload received from relay!") m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error()) return @@ -680,9 +681,9 @@ func (m *BoostService) processDenebPayload(w http.ResponseWriter, req *http.Requ for _, relay := range m.relays { wg.Add(1) - go func(relay RelayEntry) { + go func(relay types.RelayEntry) { defer wg.Done() - url := relay.GetURI(pathGetPayload) + url := relay.GetURI(params.PathGetPayload) log := log.WithField("url", url) log.Debug("calling getPayload") @@ -756,7 +757,7 @@ func (m *BoostService) processDenebPayload(w http.ResponseWriter, req *http.Requ // If no payload has been received from relay, log loudly about withholding! if getPayloadResponseIsEmpty(result) { - originRelays := RelayEntriesToStrings(originalBid.relays) + originRelays := types.RelayEntriesToStrings(originalBid.relays) log.WithField("relaysWithBid", strings.Join(originRelays, ", ")).Error("no payload received from relay!") m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error()) return @@ -801,9 +802,9 @@ func (m *BoostService) CheckRelays() int { for _, r := range m.relays { wg.Add(1) - go func(relay RelayEntry) { + go func(relay types.RelayEntry) { defer wg.Done() - url := relay.GetURI(pathStatus) + url := relay.GetURI(params.PathStatus) log := m.log.WithField("url", url) log.Debug("checking relay status") diff --git a/server/service_test.go b/server/service_test.go index 33b2438e..47d418aa 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -27,7 +27,8 @@ import ( "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" eth2UtilBellatrix "github.com/attestantio/go-eth2-client/util/bellatrix" - "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/mev-boost/server/mock" + "github.com/flashbots/mev-boost/server/types" "github.com/holiman/uint256" "github.com/prysmaticlabs/go-bitfield" "github.com/stretchr/testify/require" @@ -35,25 +36,25 @@ import ( type testBackend struct { boost *BoostService - relays []*mockRelay + relays []*mock.Relay } // newTestBackend creates a new backend, initializes mock relays, registers them and return the instance func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *testBackend { t.Helper() backend := testBackend{ - relays: make([]*mockRelay, numRelays), + relays: make([]*mock.Relay, numRelays), } - relayEntries := make([]RelayEntry, numRelays) + relayEntries := make([]types.RelayEntry, numRelays) for i := 0; i < numRelays; i++ { // Create a mock relay - backend.relays[i] = newMockRelay(t) + backend.relays[i] = mock.NewRelay(t) relayEntries[i] = backend.relays[i].RelayEntry } opts := BoostServiceOpts{ - Log: testLog, + Log: mock.TestLog, ListenAddr: "localhost:12345", Relays: relayEntries, GenesisForkVersionHex: "0x00000000", @@ -147,9 +148,9 @@ func blindedBlockContentsToPayloadDeneb(signedBlindedBlockContents *eth2ApiV1Den func TestNewBoostServiceErrors(t *testing.T) { t.Run("errors when no relays", func(t *testing.T) { _, err := NewBoostService(BoostServiceOpts{ - Log: testLog, + Log: mock.TestLog, ListenAddr: ":123", - Relays: []RelayEntry{}, + Relays: []types.RelayEntry{}, RelayMonitors: []*url.URL{}, GenesisForkVersionHex: "0x00000000", GenesisTime: 0, @@ -246,12 +247,12 @@ func TestRegisterValidator(t *testing.T) { path := "/eth/v1/builder/validators" reg := builderApiV1.SignedValidatorRegistration{ Message: &builderApiV1.ValidatorRegistration{ - FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), + FeeRecipient: mock.HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), Timestamp: time.Unix(1234356, 0), - Pubkey: _HexToPubkey( + Pubkey: mock.HexToPubkey( "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249"), }, - Signature: _HexToSignature( + Signature: mock.HexToSignature( "0x81510b571e22f89d1697545aac01c9ad0c1e7a3e778b3078bef524efae14990e58a6e960a152abd49de2e18d7fd3081c15d5c25867ccfad3d47beef6b39ac24b6b9fbf2cfa91c88f67aff750438a6841ec9e4a06a94ae41410c4f97b75ab284c"), } payload := []builderApiV1.SignedValidatorRegistration{reg} @@ -275,7 +276,7 @@ func TestRegisterValidator(t *testing.T) { require.Equal(t, 1, backend.relays[1].GetRequestCount(path)) // Now make one relay return an error - backend.relays[0].overrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { + backend.relays[0].OverrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadRequest) }) rr = backend.request(t, http.MethodPost, path, payload) @@ -284,7 +285,7 @@ func TestRegisterValidator(t *testing.T) { require.Equal(t, 2, backend.relays[1].GetRequestCount(path)) // Now make both relays return an error - which should cause the request to fail - backend.relays[1].overrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { + backend.relays[1].OverrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadRequest) }) rr = backend.request(t, http.MethodPost, path, payload) @@ -313,8 +314,8 @@ func getHeaderPath(slot uint64, parentHash phase0.Hash32, pubkey phase0.BLSPubKe } func TestGetHeader(t *testing.T) { - hash := _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") - pubkey := _HexToPubkey( + hash := mock.HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") + pubkey := mock.HexToPubkey( "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249") path := getHeaderPath(1, hash, pubkey) require.Equal(t, "/eth/v1/builder/header/1/0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", path) @@ -453,8 +454,8 @@ func TestGetHeader(t *testing.T) { } func TestGetHeaderBids(t *testing.T) { - hash := _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") - pubkey := _HexToPubkey( + hash := mock.HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") + pubkey := mock.HexToPubkey( "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249") path := getHeaderPath(2, hash, pubkey) require.Equal(t, "/eth/v1/builder/header/2/0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", path) @@ -615,9 +616,9 @@ func TestGetHeaderBids(t *testing.T) { func TestGetPayload(t *testing.T) { path := "/eth/v1/builder/blinded_blocks" - blockHash := _HexToHash("0x534809bd2b6832edff8d8ce4cb0e50068804fd1ef432c8362ad708a74fdc0e46") + blockHash := mock.HexToHash("0x534809bd2b6832edff8d8ce4cb0e50068804fd1ef432c8362ad708a74fdc0e46") payload := ð2ApiV1Capella.SignedBlindedBeaconBlock{ - Signature: _HexToSignature( + Signature: mock.HexToSignature( "0x8c795f751f812eabbabdee85100a06730a9904a4b53eedaa7f546fe0e23cd75125e293c6b0d007aa68a9da4441929d16072668abb4323bb04ac81862907357e09271fe414147b3669509d91d8ffae2ec9c789a5fcd4519629b8f2c7de8d0cce9"), Message: ð2ApiV1Capella.BlindedBeaconBlock{ Slot: 1, @@ -639,10 +640,10 @@ func TestGetPayload(t *testing.T) { Deposits: []*phase0.Deposit{}, VoluntaryExits: []*phase0.SignedVoluntaryExit{}, ExecutionPayloadHeader: &capella.ExecutionPayloadHeader{ - ParentHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), + ParentHash: mock.HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), BlockHash: blockHash, BlockNumber: 12345, - FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), + FeeRecipient: mock.HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), }, }, }, @@ -688,17 +689,17 @@ func TestGetPayload(t *testing.T) { backend := newTestBackend(t, 1, 2*time.Second) count := 0 - backend.relays[0].handlerOverrideGetPayload = func(w http.ResponseWriter, _ *http.Request) { + backend.relays[0].OverrideHandleGetPayload(func(w http.ResponseWriter, _ *http.Request) { if count > 0 { // success response on the second attempt - backend.relays[0].defaultHandleGetPayload(w) + backend.relays[0].DefaultHandleGetPayload(w) } else { w.WriteHeader(http.StatusInternalServerError) _, err := w.Write([]byte(`{"code":500,"message":"internal server error"}`)) require.NoError(t, err) } count++ - } + }) rr := backend.request(t, http.MethodPost, path, payload) require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) }) @@ -709,17 +710,17 @@ func TestGetPayload(t *testing.T) { count := 0 maxRetries := 5 - backend.relays[0].handlerOverrideGetPayload = func(w http.ResponseWriter, _ *http.Request) { + backend.relays[0].OverrideHandleGetPayload(func(w http.ResponseWriter, _ *http.Request) { count++ if count > maxRetries { // success response after max retry attempts - backend.relays[0].defaultHandleGetPayload(w) + backend.relays[0].DefaultHandleGetPayload(w) } else { w.WriteHeader(http.StatusInternalServerError) _, err := w.Write([]byte(`{"code":500,"message":"internal server error"}`)) require.NoError(t, err) } - } + }) rr := backend.request(t, http.MethodPost, path, payload) require.Equal(t, 5, backend.relays[0].GetRequestCount(path)) require.Equal(t, `{"code":502,"message":"no successful relay response"}`+"\n", rr.Body.String()) diff --git a/server/types/U256str.go b/server/types/U256str.go new file mode 100644 index 00000000..15e04b20 --- /dev/null +++ b/server/types/U256str.go @@ -0,0 +1,11 @@ +package types + +import ( + "github.com/flashbots/go-boost-utils/types" +) + +type U256Str = types.U256Str + +func IntToU256(i uint64) U256Str { + return types.IntToU256(i) +} diff --git a/server/errors.go b/server/types/errors.go similarity index 95% rename from server/errors.go rename to server/types/errors.go index a9757b38..9a57a5d8 100644 --- a/server/errors.go +++ b/server/types/errors.go @@ -1,4 +1,4 @@ -package server +package types import "errors" diff --git a/server/relay_entry.go b/server/types/relay_entry.go similarity index 90% rename from server/relay_entry.go rename to server/types/relay_entry.go index 162d98ca..cac5e692 100644 --- a/server/relay_entry.go +++ b/server/types/relay_entry.go @@ -1,4 +1,4 @@ -package server +package types import ( "bytes" @@ -22,6 +22,14 @@ func (r *RelayEntry) String() string { return r.URL.String() } +// GetURI returns the full request URI with scheme, host, path and args. +func GetURI(url *url.URL, path string) string { + u2 := *url + u2.User = nil + u2.Path = path + return u2.String() +} + // GetURI returns the full request URI with scheme, host, path and args for the relay. func (r *RelayEntry) GetURI(path string) string { return GetURI(r.URL, path) diff --git a/server/relay_entry_test.go b/server/types/relay_entry_test.go similarity index 99% rename from server/relay_entry_test.go rename to server/types/relay_entry_test.go index ffb0d182..bc637b90 100644 --- a/server/relay_entry_test.go +++ b/server/types/relay_entry_test.go @@ -1,4 +1,4 @@ -package server +package types import ( "fmt" diff --git a/server/utils.go b/server/utils.go index fb779d53..7c7fb0cb 100644 --- a/server/utils.go +++ b/server/utils.go @@ -9,7 +9,6 @@ import ( "io" "math/big" "net/http" - "net/url" "strings" "time" @@ -22,6 +21,7 @@ import ( "github.com/flashbots/go-boost-utils/bls" "github.com/flashbots/go-boost-utils/ssz" "github.com/flashbots/mev-boost/config" + "github.com/flashbots/mev-boost/server/types" "github.com/holiman/uint256" "github.com/sirupsen/logrus" ) @@ -155,20 +155,12 @@ func DecodeJSON(r io.Reader, dst any) error { return decoder.Decode(dst) } -// GetURI returns the full request URI with scheme, host, path and args. -func GetURI(url *url.URL, path string) string { - u2 := *url - u2.User = nil - u2.Path = path - return u2.String() -} - // bidResp are entries in the bids cache type bidResp struct { t time.Time response builderSpec.VersionedSignedBuilderBid bidInfo bidInfo - relays []RelayEntry + relays []types.RelayEntry } // bidRespKey is used as key for the bids cache