Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getPayload only to origin relays #342

Merged
merged 1 commit into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,22 @@ We appreciate you, friend <3.

The extended deploy steps are necessary for installations with `go install` to include the correct version.

* Ensure linter and tests are working: `make lint && make test-race`
### Before the release

Run these commands:

```bash
make lint
make test-race

# Start mev-boost with relay check, and call the mev-boost status endpoint
go run . -mainnet -relay-check -relays https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com,https://0xb3ee7afcf27f1f1259ac1787876318c6584ee353097a50ed84f51a1f21a323b3736f271a895c7ce918c038e4265918be@relay.edennetwork.io,https://0x9000009807ed12c1f08bf4e81c6da3ba8e3fc3d953898ce0102433094e5f22f21102ec057841fcb81978ed1ea0fa8246@builder-relay-mainnet.blocknative.com -debug
curl localhost:18550/eth/v1/builder/status
```


### Releasing a new version

* Update `Version` in `config/vars.go`
* it will be next_version-dev (eg. `v0.7.11-dev`)
* change it to the next version (eg. `v0.7.11`), and commit
Expand Down
10 changes: 1 addition & 9 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func Main() {
flag.Usage()
log.Fatal("no relays specified")
}
log.WithField("relays", relaysToStrings(relays)).Infof("using %d relays", len(relays))
log.WithField("relays", server.RelayEntriesToStrings(relays)).Infof("using %d relays", len(relays))

relayMonitors := parseRelayMonitorURLs(*relayMonitorURLs)
if len(relayMonitors) > 0 {
Expand Down Expand Up @@ -193,11 +193,3 @@ func parseRelayMonitorURLs(relayMonitorURLs string) (ret []*url.URL) {
}
return ret
}

func relaysToStrings(relays []server.RelayEntry) []string {
ret := []string{}
for _, entry := range relays {
ret = append(ret, entry.String())
}
return ret
}
7 changes: 4 additions & 3 deletions server/mock_relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Req

// 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, hash, publicKey string) *types.GetHeaderResponse {
func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, publicKey string) *types.GetHeaderResponse {
// Fill the payload with custom values.
message := &types.BuilderBid{
Header: &types.ExecutionPayloadHeader{
BlockHash: _HexToHash(hash),
ParentHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"),
BlockHash: _HexToHash(blockHash),
ParentHash: _HexToHash(parentHash),
},
Value: types.IntToU256(value),
Pubkey: _HexToPubkey(publicKey),
Expand Down Expand Up @@ -191,6 +191,7 @@ func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) {
response := m.MakeGetHeaderResponse(
12345,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)
if m.GetHeaderResponse != nil {
Expand Down
9 changes: 9 additions & 0 deletions server/relay_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ func NewRelayEntry(relayURL string) (entry RelayEntry, err error) {
err = entry.PublicKey.UnmarshalText([]byte(entry.URL.User.Username()))
return entry, err
}

// RelayEntriesToStrings returns the string representation of a list of relay entries
func RelayEntriesToStrings(relays []RelayEntry) []string {
ret := make([]string, len(relays))
for i, entry := range relays {
ret[i] = entry.String()
}
return ret
}
31 changes: 16 additions & 15 deletions server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,11 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
return
}

var mu sync.Mutex
relays := make(map[string][]string) // relays per blockHash
result := bidResp{}

ua := UserAgent(req.Header.Get("User-Agent"))
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

// Call the relays
var mu sync.Mutex
var wg sync.WaitGroup
for _, relay := range m.relays {
wg.Add(1)
Expand All @@ -302,7 +300,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
url := relay.GetURI(path)
log := log.WithField("url", url)
responsePayload := new(types.GetHeaderResponse)
code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, ua, nil, responsePayload)
code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, UserAgent(req.Header.Get("User-Agent")), nil, responsePayload)
if err != nil {
log.WithError(err).Warn("error making request to relay")
return
Expand Down Expand Up @@ -365,11 +363,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
defer mu.Unlock()

// Remember which relays delivered which bids (multiple relays might deliver the top bid)
if _, ok := relays[blockHash]; !ok {
relays[blockHash] = []string{relay.String()}
} else {
relays[blockHash] = append(relays[blockHash], relay.String())
}
relays[BlockHashHex(blockHash)] = append(relays[BlockHashHex(blockHash)], relay)

// Compare the bid with already known top bid (if any)
if result.response.Data != nil {
Expand Down Expand Up @@ -402,13 +396,13 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
}

// Log result
result.relays = relays[result.blockHash]
result.relays = relays[BlockHashHex(result.blockHash)]
log.WithFields(logrus.Fields{
"blockHash": result.blockHash,
"blockNumber": result.response.Data.Message.Header.BlockNumber,
"txRoot": result.response.Data.Message.Header.TransactionsRoot.String(),
"value": result.response.Data.Message.Value.String(),
"relays": strings.Join(result.relays, ", "),
"relays": strings.Join(RelayEntriesToStrings(result.relays), ", "),
}).Info("best bid")

// Remember the bid, for future logging in case of withholding
Expand Down Expand Up @@ -467,6 +461,12 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request
log.Warn("bid found but no associated relays")
}

relays := originalBid.relays
if len(relays) == 0 {
log.Warn("originating relay not found, sending getPayload request to all relays")
relays = m.relays
}

var wg sync.WaitGroup
var mu sync.Mutex
result := new(types.GetPayloadResponse)
Expand All @@ -476,7 +476,7 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request
requestCtx, requestCtxCancel := context.WithCancel(context.Background())
defer requestCtxCancel()

for _, relay := range m.relays {
for _, relay := range relays {
wg.Add(1)
go func(relay RelayEntry) {
defer wg.Done()
Expand Down Expand Up @@ -525,7 +525,8 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request

// If no payload has been received from relay, log loudly about withholding!
if result.Data == nil || result.Data.BlockHash == nilHash {
log.WithField("relays", strings.Join(originalBid.relays, ", ")).Error("no payload received from relay -- could be a network error or withholding.")
originRelays := RelayEntriesToStrings(originalBid.relays)
log.WithField("relays", strings.Join(originRelays, ", ")).Error("no payload received from relay!")
m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error())
return
}
Expand Down
60 changes: 54 additions & 6 deletions server/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,15 +234,15 @@ func TestRegisterValidator(t *testing.T) {
})
}

func TestGetHeader(t *testing.T) {
getPath := func(slot uint64, parentHash types.Hash, pubkey types.PublicKey) string {
return fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", slot, parentHash.String(), pubkey.String())
}
func getHeaderPath(slot uint64, parentHash types.Hash, pubkey types.PublicKey) string {
return fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", slot, parentHash.String(), pubkey.String())
}

func TestGetHeader(t *testing.T) {
hash := _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7")
pubkey := _HexToPubkey(
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249")
path := getPath(1, hash, pubkey)
path := getHeaderPath(1, hash, pubkey)
require.Equal(t, "/eth/v1/builder/header/1/0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", path)

t.Run("Okay response from relay", func(t *testing.T) {
Expand All @@ -257,6 +257,7 @@ func TestGetHeader(t *testing.T) {
resp := backend.relays[0].MakeGetHeaderResponse(
12345,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)
resp.Data.Message.Header.BlockHash = nilHash
Expand Down Expand Up @@ -284,20 +285,23 @@ func TestGetHeader(t *testing.T) {
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

// First relay will return signed response with value 12347.
backend.relays[1].GetHeaderResponse = backend.relays[1].MakeGetHeaderResponse(
12347,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

// First relay will return signed response with value 12346.
backend.relays[2].GetHeaderResponse = backend.relays[2].MakeGetHeaderResponse(
12346,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

Expand Down Expand Up @@ -325,18 +329,21 @@ func TestGetHeader(t *testing.T) {
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0xa38385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

backend.relays[1].GetHeaderResponse = backend.relays[1].MakeGetHeaderResponse(
12345,
"0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

backend.relays[2].GetHeaderResponse = backend.relays[2].MakeGetHeaderResponse(
12345,
"0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

Expand Down Expand Up @@ -364,6 +371,7 @@ func TestGetHeader(t *testing.T) {
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

Expand All @@ -384,6 +392,7 @@ func TestGetHeader(t *testing.T) {
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

Expand Down Expand Up @@ -432,7 +441,7 @@ func TestGetHeader(t *testing.T) {
t.Run("Invalid parent hash", func(t *testing.T) {
backend := newTestBackend(t, 1, time.Second)

invalidParentHashPath := getPath(1, types.Hash{}, pubkey)
invalidParentHashPath := getHeaderPath(1, types.Hash{}, pubkey)
rr := backend.request(t, http.MethodGet, invalidParentHashPath, nil)
require.Equal(t, http.StatusNoContent, rr.Code)
require.Equal(t, 0, backend.relays[0].GetRequestCount(path))
Expand Down Expand Up @@ -579,3 +588,42 @@ func TestGetPayloadWithTestdata(t *testing.T) {
})
}
}

func TestGetPayloadToOriginRelayOnly(t *testing.T) {
// Load the signed blinded beacon block used for getPayload
jsonFile, err := os.Open("../testdata/kiln-signed-blinded-beacon-block-899730.json")
require.NoError(t, err)
defer jsonFile.Close()
signedBlindedBeaconBlock := new(types.SignedBlindedBeaconBlock)
require.NoError(t, DecodeJSON(jsonFile, &signedBlindedBeaconBlock))

// Create a test backend with 2 relays
backend := newTestBackend(t, 2, time.Second)

// call getHeader, highest bid is returned by relay 0
getHeaderPath := "/eth/v1/builder/header/899730/0xe8b9bd82aa0e957736c5a029903e53d581edf451e28ab274f4ba314c442e35a4/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249"
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0x373fb4e59dcb659b94bd58595c25345333426aa639f821567103e2eccf34d126",
"0xe8b9bd82aa0e957736c5a029903e53d581edf451e28ab274f4ba314c442e35a4",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)
rr := backend.request(t, http.MethodGet, getHeaderPath, nil)
require.Equal(t, http.StatusOK, rr.Code, rr.Body.String())
require.Equal(t, 1, backend.relays[0].GetRequestCount(getHeaderPath))
require.Equal(t, 1, backend.relays[1].GetRequestCount(getHeaderPath))

// Prepare getPayload response
backend.relays[0].GetPayloadResponse = &types.GetPayloadResponse{
Data: &types.ExecutionPayload{
BlockHash: signedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash,
},
}

// call getPayload, ensure it's only called on relay 0 (origin of the bid)
getPayloadPath := "/eth/v1/builder/blinded_blocks"
rr = backend.request(t, http.MethodPost, getPayloadPath, signedBlindedBeaconBlock)
require.Equal(t, http.StatusOK, rr.Code, rr.Body.String())
require.Equal(t, 1, backend.relays[0].GetRequestCount(getPayloadPath))
require.Equal(t, 0, backend.relays[1].GetRequestCount(getPayloadPath))
}
5 changes: 4 additions & 1 deletion server/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
// UserAgent is a custom string type to avoid confusing url + userAgent parameters in SendHTTPRequest
type UserAgent string

// BlockHashHex is a hex-string representation of a block hash
type BlockHashHex string

// SendHTTPRequest - prepare and send HTTP request, marshaling the payload if any, and decoding the response if dst is set
func SendHTTPRequest(ctx context.Context, client http.Client, method, url string, userAgent UserAgent, payload any, dst any) (code int, err error) {
var req *http.Request
Expand Down Expand Up @@ -114,7 +117,7 @@ type bidResp struct {
t time.Time
response types.GetHeaderResponse
blockHash string
relays []string
relays []RelayEntry
}

// bidRespKey is used as key for the bids cache
Expand Down