Skip to content

Commit

Permalink
Remove wrapper types for get header response (#538)
Browse files Browse the repository at this point in the history
* Remove process bellatrix payload

* comment out unused relay monitor code

* Remove wrapper types for get header response

* fix lint

* remove replace directive
  • Loading branch information
avalonche authored Jun 27, 2023
1 parent ff8f490 commit 635800e
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 342 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ require (
require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/attestantio/go-builder-client v0.3.1
github.com/attestantio/go-builder-client v0.3.2-0.20230626105718-423f7ec4ad24
github.com/attestantio/go-eth2-client v0.17.0
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/attestantio/go-builder-client v0.3.1 h1:yKULPmv9IymY2eJ0VzdRfqjUUz/8yRO66Ym6Wn8RJM0=
github.com/attestantio/go-builder-client v0.3.1/go.mod h1:DwesMTOqnCp4u+n3uZ+fWL8wwnSBZVD9VMIVPDR+AZE=
github.com/attestantio/go-builder-client v0.3.2-0.20230626105718-423f7ec4ad24 h1:bBI7XFmYLhzVOcWG9VkU6DmjoJd51jrT84ZZuY/DZ7E=
github.com/attestantio/go-builder-client v0.3.2-0.20230626105718-423f7ec4ad24/go.mod h1:DwesMTOqnCp4u+n3uZ+fWL8wwnSBZVD9VMIVPDR+AZE=
github.com/attestantio/go-eth2-client v0.17.0 h1:Rvn/tmLHRRztoS2c/6AmsslGucyytWMvnQlAUz4EvYY=
github.com/attestantio/go-eth2-client v0.17.0/go.mod h1:N+BNIxaHmul44d0tTjwFrUJU/g6MIblFP67oagm1Lwo=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
Expand Down
53 changes: 25 additions & 28 deletions server/mock_relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (
"time"

"github.com/attestantio/go-builder-client/api"
"github.com/attestantio/go-builder-client/api/capella"
apibellatrix "github.com/attestantio/go-builder-client/api/bellatrix"
apicapella "github.com/attestantio/go-builder-client/api/capella"
"github.com/attestantio/go-builder-client/spec"
consensusspec "github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
capellaspec "github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/flashbots/go-boost-utils/bls"
Expand Down Expand Up @@ -57,7 +58,7 @@ type mockRelay struct {
handlerOverrideGetPayload func(w http.ResponseWriter, req *http.Request)

// Default responses placeholders, used if overrider does not exist
GetHeaderResponse *GetHeaderResponse
GetHeaderResponse *spec.VersionedSignedBuilderBid
GetBellatrixPayloadResponse *types.GetPayloadResponse
GetCapellaPayloadResponse *api.VersionedExecutionPayload

Expand Down Expand Up @@ -165,36 +166,34 @@ 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 consensusspec.DataVersion) *GetHeaderResponse {
func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, publicKey string, version consensusspec.DataVersion) *spec.VersionedSignedBuilderBid {
switch version {
case consensusspec.DataVersionBellatrix:
// Fill the payload with custom values.
message := &types.BuilderBid{
Header: &types.ExecutionPayloadHeader{
BlockHash: _HexToHash(blockHash),
ParentHash: _HexToHash(parentHash),
message := &apibellatrix.BuilderBid{
Header: &bellatrix.ExecutionPayloadHeader{
BlockHash: phase0.Hash32(_HexToHash(blockHash)),
ParentHash: phase0.Hash32(_HexToHash(parentHash)),
},
Value: types.IntToU256(value),
Pubkey: _HexToPubkey(publicKey),
Value: uint256.NewInt(value),
Pubkey: phase0.BLSPubKey(_HexToPubkey(publicKey)),
}

// Sign the message.
signature, err := types.SignMessage(message, types.DomainBuilder, m.secretKey)
require.NoError(m.t, err)

return &GetHeaderResponse{
Bellatrix: &types.GetHeaderResponse{
Version: "bellatrix",
Data: &types.SignedBuilderBid{
Message: message,
Signature: signature,
},
return &spec.VersionedSignedBuilderBid{
Version: consensusspec.DataVersionCapella,
Bellatrix: &apibellatrix.SignedBuilderBid{
Message: message,
Signature: phase0.BLSSignature(signature),
},
}
case consensusspec.DataVersionCapella:
// Fill the payload with custom values.
message := &capella.BuilderBid{
Header: &capellaspec.ExecutionPayloadHeader{
message := &apicapella.BuilderBid{
Header: &capella.ExecutionPayloadHeader{
BlockHash: phase0.Hash32(_HexToHash(blockHash)),
ParentHash: phase0.Hash32(_HexToHash(parentHash)),
WithdrawalsRoot: phase0.Root{},
Expand All @@ -207,13 +206,11 @@ func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, p
signature, err := types.SignMessage(message, types.DomainBuilder, m.secretKey)
require.NoError(m.t, err)

return &GetHeaderResponse{
Capella: &spec.VersionedSignedBuilderBid{
Version: consensusspec.DataVersionCapella,
Capella: &capella.SignedBuilderBid{
Message: message,
Signature: phase0.BLSSignature(signature),
},
return &spec.VersionedSignedBuilderBid{
Version: consensusspec.DataVersionCapella,
Capella: &apicapella.SignedBuilderBid{
Message: message,
Signature: phase0.BLSSignature(signature),
},
}
case consensusspec.DataVersionAltair, consensusspec.DataVersionPhase0, consensusspec.DataVersionDeneb:
Expand Down Expand Up @@ -263,12 +260,12 @@ func (m *mockRelay) defaultHandleGetHeader(w http.ResponseWriter) {
func (m *mockRelay) MakeGetPayloadResponse(parentHash, blockHash, feeRecipient string, blockNumber uint64) *api.VersionedExecutionPayload {
return &api.VersionedExecutionPayload{
Version: consensusspec.DataVersionCapella,
Capella: &capellaspec.ExecutionPayload{
Capella: &capella.ExecutionPayload{
ParentHash: phase0.Hash32(_HexToHash(parentHash)),
BlockHash: phase0.Hash32(_HexToHash(blockHash)),
BlockNumber: blockNumber,
FeeRecipient: bellatrix.ExecutionAddress(_HexToAddress(feeRecipient)),
Withdrawals: make([]*capellaspec.Withdrawal, 0),
Withdrawals: make([]*capella.Withdrawal, 0),
},
}
}
Expand Down
80 changes: 46 additions & 34 deletions server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import (
"time"

"github.com/attestantio/go-builder-client/api"
"github.com/attestantio/go-builder-client/spec"
"github.com/attestantio/go-eth2-client/api/v1/capella"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/flashbots/go-boost-utils/types"
"github.com/flashbots/go-utils/httplogger"
"github.com/flashbots/mev-boost/config"
Expand All @@ -35,7 +37,7 @@ var (
)

var (
nilHash = types.Hash{}
nilHash = phase0.Hash32{}
nilResponse = struct{}{}
)

Expand All @@ -46,7 +48,7 @@ type httpErrorResp struct {

// AuctionTranscript is the bid and blinded block received from the relay send to the relay monitor
type AuctionTranscript struct {
Bid *SignedBuilderBid `json:"bid"`
Bid *spec.VersionedSignedBuilderBid // TODO: proper json marshalling and unmashalling
Acceptance *types.SignedBlindedBeaconBlock `json:"acceptance"`
}

Expand Down Expand Up @@ -369,7 +371,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
path := fmt.Sprintf("/eth/v1/builder/header/%s/%s/%s", slot, parentHashHex, pubkey)
url := relay.GetURI(path)
log := log.WithField("url", url)
responsePayload := new(GetHeaderResponse)
responsePayload := new(spec.VersionedSignedBuilderBid)
code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, ua, headers, nil, responsePayload)
if err != nil {
log.WithError(err).Warn("error making request to relay")
Expand All @@ -381,28 +383,39 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
return
}

// Skip if invalid payload
if responsePayload.IsInvalid() {
// Skip if payload is empty
if responsePayload.IsEmpty() {
return
}

blockHash := responsePayload.BlockHash()
valueEth := weiBigIntToEthBigFloat(responsePayload.Value())
// Getting the bid info will check if there are missing fields in the response
bidInfo, err := parseBidInfo(responsePayload)
if err != nil {
log.WithError(err).Warn("error parsing bid info")
return
}

if bidInfo.blockHash == nilHash {
log.Warn("relay responded with empty block hash")
return
}

valueEth := weiBigIntToEthBigFloat(bidInfo.value.ToBig())
log = log.WithFields(logrus.Fields{
"blockNumber": responsePayload.BlockNumber(),
"blockHash": blockHash,
"txRoot": responsePayload.TransactionsRoot(),
"blockNumber": bidInfo.blockNumber,
"blockHash": bidInfo.blockHash.String(),
"txRoot": bidInfo.txRoot.String(),
"value": valueEth.Text('f', 18),
})

if relay.PublicKey.String() != responsePayload.Pubkey() {
log.Errorf("bid pubkey mismatch. expected: %s - got: %s", relay.PublicKey.String(), responsePayload.Pubkey())
if relay.PublicKey.String() != bidInfo.pubkey.String() {
log.Errorf("bid pubkey mismatch. expected: %s - got: %s", relay.PublicKey.String(), bidInfo.pubkey.String())
return
}

// Verify the relay signature in the relay response
if !config.SkipRelaySignatureCheck {
ok, err := types.VerifySignature(responsePayload.Message(), m.builderSigningDomain, relay.PublicKey[:], responsePayload.Signature())
ok, err := checkRelaySignature(responsePayload, m.builderSigningDomain, relay.PublicKey)
if err != nil {
log.WithError(err).Error("error verifying relay signature")
return
Expand All @@ -414,25 +427,24 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
}

// Verify response coherence with proposer's input data
responseParentHash := responsePayload.ParentHash()
if responseParentHash != parentHashHex {
if bidInfo.parentHash.String() != parentHashHex {
log.WithFields(logrus.Fields{
"originalParentHash": parentHashHex,
"responseParentHash": responseParentHash,
"responseParentHash": bidInfo.parentHash.String(),
}).Error("proposer and relay parent hashes are not the same")
return
}

isZeroValue := responsePayload.Value().String() == "0"
isEmptyListTxRoot := responsePayload.TransactionsRoot() == "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1"
isZeroValue := bidInfo.value.String() == "0"
isEmptyListTxRoot := bidInfo.txRoot.String() == "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1"
if isZeroValue || isEmptyListTxRoot {
log.Warn("ignoring bid with 0 value")
return
}
log.Debug("bid received")

// Skip if value (fee) is lower than the minimum bid
if responsePayload.Value().Cmp(m.relayMinBid.BigInt()) == -1 {
if bidInfo.value.ToBig().Cmp(m.relayMinBid.BigInt()) == -1 {
log.Debug("ignoring bid below min-bid value")
return
}
Expand All @@ -441,16 +453,16 @@ 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)
relays[BlockHashHex(blockHash)] = append(relays[BlockHashHex(blockHash)], relay)
relays[BlockHashHex(bidInfo.blockHash.String())] = append(relays[BlockHashHex(bidInfo.blockHash.String())], relay)

// Compare the bid with already known top bid (if any)
if !result.response.IsEmpty() {
valueDiff := responsePayload.Value().Cmp(result.response.Value())
valueDiff := bidInfo.value.Cmp(result.bidInfo.value)
if valueDiff == -1 { // current bid is less profitable than already known one
return
} else if valueDiff == 0 { // current bid is equally profitable as already known one. Use hash as tiebreaker
previousBidBlockHash := result.response.BlockHash()
if blockHash >= previousBidBlockHash {
previousBidBlockHash := result.bidInfo.blockHash
if bidInfo.blockHash.String() >= previousBidBlockHash.String() {
return
}
}
Expand All @@ -459,33 +471,33 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
// Use this relay's response as mev-boost response because it's most profitable
log.Debug("new best bid")
result.response = *responsePayload
result.blockHash = blockHash
result.bidInfo = bidInfo
result.t = time.Now()
}(relay)
}

// Wait for all requests to complete...
wg.Wait()

if result.blockHash == "" {
if result.response.IsEmpty() {
log.Info("no bid received")
w.WriteHeader(http.StatusNoContent)
return
}

// Log result
valueEth := weiBigIntToEthBigFloat(result.response.Value())
result.relays = relays[BlockHashHex(result.blockHash)]
valueEth := weiBigIntToEthBigFloat(result.bidInfo.value.ToBig())
result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())]
log.WithFields(logrus.Fields{
"blockHash": result.blockHash,
"blockNumber": result.response.BlockNumber(),
"txRoot": result.response.TransactionsRoot(),
"blockHash": result.bidInfo.blockHash.String(),
"blockNumber": result.bidInfo.blockNumber,
"txRoot": result.bidInfo.txRoot,
"value": valueEth.Text('f', 18),
"relays": strings.Join(RelayEntriesToStrings(result.relays), ", "),
}).Info("best bid")

// Remember the bid, for future logging in case of withholding
bidKey := bidRespKey{slot: _slot, blockHash: result.blockHash}
bidKey := bidRespKey{slot: _slot, blockHash: result.bidInfo.blockHash.String()}
m.bidsLock.Lock()
m.bids[bidKey] = result
m.bidsLock.Unlock()
Expand Down Expand Up @@ -535,7 +547,7 @@ func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Re
m.bidsLock.Lock()
originalBid := m.bids[bidKey]
m.bidsLock.Unlock()
if originalBid.blockHash == "" {
if originalBid.response.IsEmpty() {
log.Error("no bid for this getPayload payload found. was getHeader called before?")
} else if len(originalBid.relays) == 0 {
log.Warn("bid found but no associated relays")
Expand Down Expand Up @@ -575,7 +587,7 @@ func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Re
return
}

if responsePayload.Capella == nil || types.Hash(responsePayload.Capella.BlockHash) == nilHash {
if responsePayload.Capella == nil || responsePayload.Capella.BlockHash == nilHash {
log.Error("response with empty data!")
return
}
Expand Down Expand Up @@ -618,7 +630,7 @@ func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Re
wg.Wait()

// If no payload has been received from relay, log loudly about withholding!
if result.Capella == nil || types.Hash(result.Capella.BlockHash) == nilHash {
if result.Capella == nil || result.Capella.BlockHash == nilHash {
originRelays := RelayEntriesToStrings(originalBid.relays)
log.WithField("relaysWithBid", strings.Join(originRelays, ", ")).Error("no payload received from relay!")
m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error())
Expand Down
Loading

0 comments on commit 635800e

Please sign in to comment.