Skip to content

Commit

Permalink
Minor cleanup (#715)
Browse files Browse the repository at this point in the history
* server: refactor bidRespKey

* server: split http handling and functionality
  • Loading branch information
MariusVanDerWijden authored Jan 31, 2025
1 parent 245de7f commit a32a1bf
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 180 deletions.
176 changes: 176 additions & 0 deletions server/functionality.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package server

import (
"context"
"fmt"
"net/http"
"sync"
"time"

builderSpec "github.com/attestantio/go-builder-client/spec"
"github.com/flashbots/mev-boost/config"
"github.com/flashbots/mev-boost/server/types"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)

func (m *BoostService) getHeader(log *logrus.Entry, ua UserAgent, _slot uint64, pubkey, parentHashHex string) (bidResp, error) {
if len(pubkey) != 98 {
return bidResp{}, errInvalidPubkey
}

if len(parentHashHex) != 66 {
return bidResp{}, errInvalidHash
}

// Make sure we have a uid for this slot
m.slotUIDLock.Lock()
if m.slotUID.slot < _slot {
m.slotUID.slot = _slot
m.slotUID.uid = uuid.New()
}
slotUID := m.slotUID.uid
m.slotUIDLock.Unlock()
log = log.WithField("slotUID", slotUID)

// Log how late into the slot the request starts
slotStartTimestamp := m.genesisTime + _slot*config.SlotTimeSec
msIntoSlot := uint64(time.Now().UTC().UnixMilli()) - slotStartTimestamp*1000
log.WithFields(logrus.Fields{
"genesisTime": m.genesisTime,
"slotTimeSec": config.SlotTimeSec,
"msIntoSlot": msIntoSlot,
}).Infof("getHeader request start - %d milliseconds into slot %d", msIntoSlot, _slot)
// Add request headers
headers := map[string]string{
HeaderKeySlotUID: slotUID.String(),
HeaderStartTimeUnixMS: fmt.Sprintf("%d", time.Now().UTC().UnixMilli()),
}
// Prepare relay responses
var (
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

mu sync.Mutex
wg sync.WaitGroup
)

// Call the relays
for _, relay := range m.relays {
wg.Add(1)
go func(relay types.RelayEntry) {
defer wg.Done()
path := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", _slot, parentHashHex, pubkey)
url := relay.GetURI(path)
log := log.WithField("url", url)
responsePayload := new(builderSpec.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")
return
}

if code == http.StatusNoContent {
log.Debug("no-content response")
return
}

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

// 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": bidInfo.blockNumber,
"blockHash": bidInfo.blockHash.String(),
"txRoot": bidInfo.txRoot.String(),
"value": valueEth.Text('f', 18),
})

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 := checkRelaySignature(responsePayload, m.builderSigningDomain, relay.PublicKey)
if err != nil {
log.WithError(err).Error("error verifying relay signature")
return
}
if !ok {
log.Error("failed to verify relay signature")
return
}
}

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

isZeroValue := bidInfo.value.IsZero()
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 bidInfo.value.CmpBig(m.relayMinBid.BigInt()) == -1 {
log.Debug("ignoring bid below min-bid value")
return
}

mu.Lock()
defer mu.Unlock()

// Remember which relays delivered which bids (multiple relays might deliver the top bid)
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 := 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.bidInfo.blockHash
if bidInfo.blockHash.String() >= previousBidBlockHash.String() {
return
}
}
}

// Use this relay's response as mev-boost response because it's most profitable
log.Debug("new best bid")
result.response = *responsePayload
result.bidInfo = bidInfo
result.t = time.Now()
}(relay)
}
// Wait for all requests to complete...
wg.Wait()

// Set the winning relay before returning
result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())]
return result, nil
}
Loading

0 comments on commit a32a1bf

Please sign in to comment.