Skip to content

Commit a32a1bf

Browse files
Minor cleanup (#715)
* server: refactor bidRespKey * server: split http handling and functionality
1 parent 245de7f commit a32a1bf

File tree

3 files changed

+204
-180
lines changed

3 files changed

+204
-180
lines changed

server/functionality.go

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"sync"
8+
"time"
9+
10+
builderSpec "github.com/attestantio/go-builder-client/spec"
11+
"github.com/flashbots/mev-boost/config"
12+
"github.com/flashbots/mev-boost/server/types"
13+
"github.com/google/uuid"
14+
"github.com/sirupsen/logrus"
15+
)
16+
17+
func (m *BoostService) getHeader(log *logrus.Entry, ua UserAgent, _slot uint64, pubkey, parentHashHex string) (bidResp, error) {
18+
if len(pubkey) != 98 {
19+
return bidResp{}, errInvalidPubkey
20+
}
21+
22+
if len(parentHashHex) != 66 {
23+
return bidResp{}, errInvalidHash
24+
}
25+
26+
// Make sure we have a uid for this slot
27+
m.slotUIDLock.Lock()
28+
if m.slotUID.slot < _slot {
29+
m.slotUID.slot = _slot
30+
m.slotUID.uid = uuid.New()
31+
}
32+
slotUID := m.slotUID.uid
33+
m.slotUIDLock.Unlock()
34+
log = log.WithField("slotUID", slotUID)
35+
36+
// Log how late into the slot the request starts
37+
slotStartTimestamp := m.genesisTime + _slot*config.SlotTimeSec
38+
msIntoSlot := uint64(time.Now().UTC().UnixMilli()) - slotStartTimestamp*1000
39+
log.WithFields(logrus.Fields{
40+
"genesisTime": m.genesisTime,
41+
"slotTimeSec": config.SlotTimeSec,
42+
"msIntoSlot": msIntoSlot,
43+
}).Infof("getHeader request start - %d milliseconds into slot %d", msIntoSlot, _slot)
44+
// Add request headers
45+
headers := map[string]string{
46+
HeaderKeySlotUID: slotUID.String(),
47+
HeaderStartTimeUnixMS: fmt.Sprintf("%d", time.Now().UTC().UnixMilli()),
48+
}
49+
// Prepare relay responses
50+
var (
51+
result = bidResp{} // the final response, containing the highest bid (if any)
52+
relays = make(map[BlockHashHex][]types.RelayEntry) // relays that sent the bid for a specific blockHash
53+
54+
mu sync.Mutex
55+
wg sync.WaitGroup
56+
)
57+
58+
// Call the relays
59+
for _, relay := range m.relays {
60+
wg.Add(1)
61+
go func(relay types.RelayEntry) {
62+
defer wg.Done()
63+
path := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", _slot, parentHashHex, pubkey)
64+
url := relay.GetURI(path)
65+
log := log.WithField("url", url)
66+
responsePayload := new(builderSpec.VersionedSignedBuilderBid)
67+
code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, ua, headers, nil, responsePayload)
68+
if err != nil {
69+
log.WithError(err).Warn("error making request to relay")
70+
return
71+
}
72+
73+
if code == http.StatusNoContent {
74+
log.Debug("no-content response")
75+
return
76+
}
77+
78+
// Skip if payload is empty
79+
if responsePayload.IsEmpty() {
80+
return
81+
}
82+
83+
// Getting the bid info will check if there are missing fields in the response
84+
bidInfo, err := parseBidInfo(responsePayload)
85+
if err != nil {
86+
log.WithError(err).Warn("error parsing bid info")
87+
return
88+
}
89+
90+
if bidInfo.blockHash == nilHash {
91+
log.Warn("relay responded with empty block hash")
92+
return
93+
}
94+
95+
valueEth := weiBigIntToEthBigFloat(bidInfo.value.ToBig())
96+
log = log.WithFields(logrus.Fields{
97+
"blockNumber": bidInfo.blockNumber,
98+
"blockHash": bidInfo.blockHash.String(),
99+
"txRoot": bidInfo.txRoot.String(),
100+
"value": valueEth.Text('f', 18),
101+
})
102+
103+
if relay.PublicKey.String() != bidInfo.pubkey.String() {
104+
log.Errorf("bid pubkey mismatch. expected: %s - got: %s", relay.PublicKey.String(), bidInfo.pubkey.String())
105+
return
106+
}
107+
108+
// Verify the relay signature in the relay response
109+
if !config.SkipRelaySignatureCheck {
110+
ok, err := checkRelaySignature(responsePayload, m.builderSigningDomain, relay.PublicKey)
111+
if err != nil {
112+
log.WithError(err).Error("error verifying relay signature")
113+
return
114+
}
115+
if !ok {
116+
log.Error("failed to verify relay signature")
117+
return
118+
}
119+
}
120+
121+
// Verify response coherence with proposer's input data
122+
if bidInfo.parentHash.String() != parentHashHex {
123+
log.WithFields(logrus.Fields{
124+
"originalParentHash": parentHashHex,
125+
"responseParentHash": bidInfo.parentHash.String(),
126+
}).Error("proposer and relay parent hashes are not the same")
127+
return
128+
}
129+
130+
isZeroValue := bidInfo.value.IsZero()
131+
isEmptyListTxRoot := bidInfo.txRoot.String() == "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1"
132+
if isZeroValue || isEmptyListTxRoot {
133+
log.Warn("ignoring bid with 0 value")
134+
return
135+
}
136+
log.Debug("bid received")
137+
138+
// Skip if value (fee) is lower than the minimum bid
139+
if bidInfo.value.CmpBig(m.relayMinBid.BigInt()) == -1 {
140+
log.Debug("ignoring bid below min-bid value")
141+
return
142+
}
143+
144+
mu.Lock()
145+
defer mu.Unlock()
146+
147+
// Remember which relays delivered which bids (multiple relays might deliver the top bid)
148+
relays[BlockHashHex(bidInfo.blockHash.String())] = append(relays[BlockHashHex(bidInfo.blockHash.String())], relay)
149+
150+
// Compare the bid with already known top bid (if any)
151+
if !result.response.IsEmpty() {
152+
valueDiff := bidInfo.value.Cmp(result.bidInfo.value)
153+
if valueDiff == -1 { // current bid is less profitable than already known one
154+
return
155+
} else if valueDiff == 0 { // current bid is equally profitable as already known one. Use hash as tiebreaker
156+
previousBidBlockHash := result.bidInfo.blockHash
157+
if bidInfo.blockHash.String() >= previousBidBlockHash.String() {
158+
return
159+
}
160+
}
161+
}
162+
163+
// Use this relay's response as mev-boost response because it's most profitable
164+
log.Debug("new best bid")
165+
result.response = *responsePayload
166+
result.bidInfo = bidInfo
167+
result.t = time.Now()
168+
}(relay)
169+
}
170+
// Wait for all requests to complete...
171+
wg.Wait()
172+
173+
// Set the winning relay before returning
174+
result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())]
175+
return result, nil
176+
}

0 commit comments

Comments
 (0)