generated from flashbots/flashbots-repository-template
-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* server: refactor bidRespKey * server: split http handling and functionality
- Loading branch information
1 parent
245de7f
commit a32a1bf
Showing
3 changed files
with
204 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.