From 9ce16a0c5976f9c0bab11deae6c5fb26478dff8f Mon Sep 17 00:00:00 2001 From: MrKoberman Date: Tue, 28 May 2024 15:18:07 +0100 Subject: [PATCH] feat: privileged-builders Privileged builders are a list of public keys of builders from which bids will be accepted first, even if the bid is lower. If no bids are received from the privileged builders, bids from other builders will be accepted. This is useful when you have a special contract with specific relays and you would like to have their blocks used instead of other relays. While still having other relays as a fallback. --- README.md | 4 ++ cli/main.go | 54 +++++++++++------ cli/types.go | 34 +++++++++++ server/mock/mock_relay.go | 20 +++---- server/service.go | 118 ++++++++++++++++++++++++++------------ server/service_test.go | 73 ++++++++++++++++++----- 6 files changed, 222 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 41111059..9f53bcef 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,10 @@ Usage of mev-boost: relay monitor urls - single entry or comma-separated list (scheme://host) -relays string relay urls - single entry or comma-separated list (scheme://pubkey@host) + -privileged-builder + a single privileged builder(relay pubkey), can be specified multiple times + -privileged-builders string + privileged builders(relay pubkey) - single entry or comma-separated list -request-timeout-getheader int timeout for getHeader requests to the relay [ms] (default 950) -request-timeout-getpayload int diff --git a/cli/main.go b/cli/main.go index cc16b1bb..fb258790 100644 --- a/cli/main.go +++ b/cli/main.go @@ -32,17 +32,18 @@ var ( errInvalidLoglevel = errors.New("invalid loglevel") // defaults - defaultLogJSON = os.Getenv("LOG_JSON") != "" - defaultLogLevel = common.GetEnv("LOG_LEVEL", "info") - defaultListenAddr = common.GetEnv("BOOST_LISTEN_ADDR", "localhost:18550") - defaultRelayCheck = os.Getenv("RELAY_STARTUP_CHECK") != "" - defaultRelayMinBidEth = common.GetEnvFloat64("MIN_BID_ETH", 0) - defaultDisableLogVersion = os.Getenv("DISABLE_LOG_VERSION") == "1" // disables adding the version to every log entry - defaultDebug = os.Getenv("DEBUG") != "" - defaultLogServiceTag = os.Getenv("LOG_SERVICE_TAG") - defaultRelays = os.Getenv("RELAYS") - defaultRelayMonitors = os.Getenv("RELAY_MONITORS") - defaultMaxRetries = common.GetEnvInt("REQUEST_MAX_RETRIES", 5) + defaultLogJSON = os.Getenv("LOG_JSON") != "" + defaultLogLevel = common.GetEnv("LOG_LEVEL", "info") + defaultListenAddr = common.GetEnv("BOOST_LISTEN_ADDR", "localhost:18550") + defaultRelayCheck = os.Getenv("RELAY_STARTUP_CHECK") != "" + defaultRelayMinBidEth = common.GetEnvFloat64("MIN_BID_ETH", 0) + defaultDisableLogVersion = os.Getenv("DISABLE_LOG_VERSION") == "1" // disables adding the version to every log entry + defaultDebug = os.Getenv("DEBUG") != "" + defaultLogServiceTag = os.Getenv("LOG_SERVICE_TAG") + defaultRelays = os.Getenv("RELAYS") + defaultRelayMonitors = os.Getenv("RELAY_MONITORS") + defaultMaxRetries = common.GetEnvInt("REQUEST_MAX_RETRIES", 5) + defaultPrivilegedBuilders = os.Getenv("PRIVILEGED_BUILDERS") defaultGenesisForkVersion = common.GetEnv("GENESIS_FORK_VERSION", "") defaultGenesisTime = common.GetEnvInt("GENESIS_TIMESTAMP", -1) @@ -55,8 +56,9 @@ var ( defaultTimeoutMsGetPayload = common.GetEnvInt("RELAY_TIMEOUT_MS_GETPAYLOAD", 4000) // timeout for getPayload requests defaultTimeoutMsRegisterValidator = common.GetEnvInt("RELAY_TIMEOUT_MS_REGVAL", 3000) // timeout for registerValidator requests - relays relayList - relayMonitors relayMonitorList + relays relayList + relayMonitors relayMonitorList + privilegedBuilders privilegedBuilderList // cli flags printVersion = flag.Bool("version", false, "only print version") @@ -66,11 +68,12 @@ var ( logService = flag.String("log-service", defaultLogServiceTag, "add a 'service=...' tag to all log messages") logNoVersion = flag.Bool("log-no-version", defaultDisableLogVersion, "disables adding the version to every log entry") - listenAddr = flag.String("addr", defaultListenAddr, "listen-address for mev-boost server") - relayURLs = flag.String("relays", defaultRelays, "relay urls - single entry or comma-separated list (scheme://pubkey@host)") - relayCheck = flag.Bool("relay-check", defaultRelayCheck, "check relay status on startup and on the status API call") - relayMinBidEth = flag.Float64("min-bid", defaultRelayMinBidEth, "minimum bid to accept from a relay [eth]") - relayMonitorURLs = flag.String("relay-monitors", defaultRelayMonitors, "relay monitor urls - single entry or comma-separated list (scheme://host)") + listenAddr = flag.String("addr", defaultListenAddr, "listen-address for mev-boost server") + relayURLs = flag.String("relays", defaultRelays, "relay urls - single entry or comma-separated list (scheme://pubkey@host)") + relayCheck = flag.Bool("relay-check", defaultRelayCheck, "check relay status on startup and on the status API call") + relayMinBidEth = flag.Float64("min-bid", defaultRelayMinBidEth, "minimum bid to accept from a relay [eth]") + relayMonitorURLs = flag.String("relay-monitors", defaultRelayMonitors, "relay monitor urls - single entry or comma-separated list (scheme://host)") + privilegedBuilderKeys = flag.String("privileged-builders", defaultPrivilegedBuilders, "single entry or comma-separated list of relay username (pubkey)") relayTimeoutMsGetHeader = flag.Int("request-timeout-getheader", defaultTimeoutMsGetHeader, "timeout for getHeader requests to the relay [ms]") relayTimeoutMsGetPayload = flag.Int("request-timeout-getpayload", defaultTimeoutMsGetPayload, "timeout for getPayload requests to the relay [ms]") @@ -95,6 +98,7 @@ func Main() { // process repeatable flags flag.Var(&relays, "relay", "a single relay, can be specified multiple times") flag.Var(&relayMonitors, "relay-monitor", "a single relay monitor, can be specified multiple times") + flag.Var(&privilegedBuilders, "privileged-builder", "a single privileged builder, can be specified multiple times") // parse flags and get started flag.Parse() @@ -149,13 +153,24 @@ func Main() { } } + // set relay priorities + if *privilegedBuilderKeys != "" { + for _, builderKey := range strings.Split(*privilegedBuilderKeys, ",") { + err := privilegedBuilders.Set(strings.TrimSpace(builderKey)) + if err != nil { + log.WithError(err).WithField("privilegedBuilder", builderKey).Fatal("Invalid privileged builder") + } + } + } + if len(relays) == 0 { flag.Usage() log.Fatal("no relays specified") } log.Infof("using %d relays", len(relays)) for index, relay := range relays { - log.Infof("relay #%d: %s", index+1, relay.String()) + isPrivileged := privilegedBuilders.Contains(relay.PublicKey) + log.Infof("relay #%d: %s, privileged %t", index+1, relay.String(), isPrivileged) } // For backwards compatibility with the -relay-monitors flag. @@ -188,6 +203,7 @@ func Main() { ListenAddr: *listenAddr, Relays: relays, RelayMonitors: relayMonitors, + PrivilegedBuilders: privilegedBuilders, GenesisForkVersionHex: genesisForkVersionHex, GenesisTime: genesisTime, RelayCheck: *relayCheck, diff --git a/cli/types.go b/cli/types.go index 28c99b39..1b6cde8b 100644 --- a/cli/types.go +++ b/cli/types.go @@ -1,10 +1,13 @@ package cli import ( + "bytes" "errors" "net/url" "strings" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/flashbots/go-boost-utils/utils" "github.com/flashbots/mev-boost/server/types" ) @@ -67,3 +70,34 @@ func (rm *relayMonitorList) Set(value string) error { *rm = append(*rm, relayMonitor) return nil } + +type privilegedBuilderList []phase0.BLSPubKey + +func (pb *privilegedBuilderList) String() string { + privilegedBuilders := []string{} + for _, privilegedBuilder := range *pb { + privilegedBuilders = append(privilegedBuilders, privilegedBuilder.String()) + } + return strings.Join(privilegedBuilders, ",") +} + +func (pb *privilegedBuilderList) Contains(privilegedBuilder phase0.BLSPubKey) bool { + for _, entry := range *pb { + if bytes.Equal(entry[:], privilegedBuilder[:]) { + return true + } + } + return false +} + +func (pb *privilegedBuilderList) Set(value string) error { + privilegedBuilder, err := utils.HexToPubkey(value) + if err != nil { + return err + } + if pb.Contains(privilegedBuilder) { + return errDuplicateEntry + } + *pb = append(*pb, privilegedBuilder) + return nil +} diff --git a/server/mock/mock_relay.go b/server/mock/mock_relay.go index c74d2c87..8be753b9 100644 --- a/server/mock/mock_relay.go +++ b/server/mock/mock_relay.go @@ -29,16 +29,6 @@ import ( "github.com/stretchr/testify/require" ) -const ( - mockRelaySecretKeyHex = "0x4e343a647c5a5c44d76c2c58b63f02cdf3a9a0ec40f102ebc26363b4b1b95033" -) - -var ( - skBytes, _ = hexutil.Decode(mockRelaySecretKeyHex) - mockRelaySecretKey, _ = bls.SecretKeyFromBytes(skBytes) - mockRelayPublicKey, _ = bls.PublicKeyFromSecretKey(mockRelaySecretKey) -) - // Relay is used to fake a relay's behavior. // You can override each of its handler by setting the instance's HandlerOverride_METHOD_TO_OVERRIDE to your own // handler. @@ -73,7 +63,11 @@ type Relay struct { // A secret key must be provided to sign default and custom response messages func NewRelay(t *testing.T) *Relay { t.Helper() - relay := &Relay{t: t, secretKey: mockRelaySecretKey, publicKey: mockRelayPublicKey, requestCount: make(map[string]int)} + + relay := &Relay{t: t, requestCount: make(map[string]int)} + + relay.secretKey, _, _ = bls.GenerateNewKeypair() + relay.publicKey, _ = bls.PublicKeyFromSecretKey(relay.secretKey) // Initialize server relay.Server = httptest.NewServer(relay.getRouter()) @@ -81,7 +75,7 @@ func NewRelay(t *testing.T) *Relay { // Create the RelayEntry with correct pubkey url, err := url.Parse(relay.Server.URL) require.NoError(t, err) - urlWithKey := fmt.Sprintf("%s://%s@%s", url.Scheme, hexutil.Encode(bls.PublicKeyToBytes(mockRelayPublicKey)), url.Host) + urlWithKey := fmt.Sprintf("%s://%s@%s", url.Scheme, hexutil.Encode(bls.PublicKeyToBytes(relay.publicKey)), url.Host) relay.RelayEntry, err = types.NewRelayEntry(urlWithKey) require.NoError(t, err) return relay @@ -248,7 +242,7 @@ func (m *Relay) defaultHandleGetHeader(w http.ResponseWriter) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + m.RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) if m.GetHeaderResponse != nil { diff --git a/server/service.go b/server/service.go index 73aa21fd..c1423288 100644 --- a/server/service.go +++ b/server/service.go @@ -62,6 +62,7 @@ type BoostServiceOpts struct { ListenAddr string Relays []types.RelayEntry RelayMonitors []*url.URL + PrivilegedBuilders []phase0.BLSPubKey GenesisForkVersionHex string GenesisTime uint64 RelayCheck bool @@ -75,14 +76,15 @@ type BoostServiceOpts struct { // BoostService - the mev-boost service type BoostService struct { - listenAddr string - relays []types.RelayEntry - relayMonitors []*url.URL - log *logrus.Entry - srv *http.Server - relayCheck bool - relayMinBid types.U256Str - genesisTime uint64 + listenAddr string + relays []types.RelayEntry + relayMonitors []*url.URL + privilegedBuilders []phase0.BLSPubKey + log *logrus.Entry + srv *http.Server + relayCheck bool + relayMinBid types.U256Str + genesisTime uint64 builderSigningDomain phase0.Domain httpClientGetHeader http.Client @@ -109,15 +111,16 @@ func NewBoostService(opts BoostServiceOpts) (*BoostService, error) { } return &BoostService{ - listenAddr: opts.ListenAddr, - relays: opts.Relays, - relayMonitors: opts.RelayMonitors, - log: opts.Log, - relayCheck: opts.RelayCheck, - relayMinBid: opts.RelayMinBid, - genesisTime: opts.GenesisTime, - bids: make(map[bidRespKey]bidResp), - slotUID: &slotUID{}, + listenAddr: opts.ListenAddr, + relays: opts.Relays, + relayMonitors: opts.RelayMonitors, + privilegedBuilders: opts.PrivilegedBuilders, + log: opts.Log, + relayCheck: opts.RelayCheck, + relayMinBid: opts.RelayMinBid, + genesisTime: opts.GenesisTime, + bids: make(map[bidRespKey]bidResp), + slotUID: &slotUID{}, builderSigningDomain: builderSigningDomain, httpClientGetHeader: http.Client{ @@ -292,7 +295,7 @@ func (m *BoostService) handleRegisterValidator(w http.ResponseWriter, req *http. } // handleGetHeader requests bids from the relays -func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) { +func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) { //nolint:maintidx vars := mux.Vars(req) slot := vars["slot"] parentHashHex := vars["parent_hash"] @@ -349,6 +352,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) } // Prepare relay responses result := bidResp{} // the final response, containing the highest bid (if any) + resultPrivileged := bidResp{} // the final response, containing the highest bid (if any) for privileged relays relays := make(map[BlockHashHex][]types.RelayEntry) // relays that sent the bid for a specific blockHash // Call the relays var mu sync.Mutex @@ -444,35 +448,46 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) // 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 - } - } + if m.isPrivilegedRelay(relay.PublicKey) { + m.setBestBid(&resultPrivileged, bidInfo, responsePayload, log) + } else { + m.setBestBid(&result, bidInfo, responsePayload, log) } - - // 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() - if result.response.IsEmpty() { + if resultPrivileged.response.IsEmpty() && result.response.IsEmpty() { log.Info("no bid received") w.WriteHeader(http.StatusNoContent) return } + if !resultPrivileged.response.IsEmpty() { + // Log result privileged + valueEth := weiBigIntToEthBigFloat(resultPrivileged.bidInfo.value.ToBig()) + resultPrivileged.relays = relays[BlockHashHex(resultPrivileged.bidInfo.blockHash.String())] + log.WithFields(logrus.Fields{ + "blockHash": resultPrivileged.bidInfo.blockHash.String(), + "blockNumber": resultPrivileged.bidInfo.blockNumber, + "txRoot": resultPrivileged.bidInfo.txRoot.String(), + "value": valueEth.Text('f', 18), + "relays": strings.Join(types.RelayEntriesToStrings(resultPrivileged.relays), ", "), + "privileged": true, + }).Info("best privileged bid") + + // Remember the bid, for future logging in case of withholding + bidKey := bidRespKey{slot: _slot, blockHash: resultPrivileged.bidInfo.blockHash.String()} + m.bidsLock.Lock() + m.bids[bidKey] = resultPrivileged + m.bidsLock.Unlock() + + // Return the bid + m.respondOK(w, &resultPrivileged.response) + return + } + // Log result valueEth := weiBigIntToEthBigFloat(result.bidInfo.value.ToBig()) result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())] @@ -482,6 +497,7 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) "txRoot": result.bidInfo.txRoot.String(), "value": valueEth.Text('f', 18), "relays": strings.Join(types.RelayEntriesToStrings(result.relays), ", "), + "privileged": false, }).Info("best bid") // Remember the bid, for future logging in case of withholding @@ -494,6 +510,27 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) m.respondOK(w, &result.response) } +func (m *BoostService) setBestBid(result *bidResp, bidInfo bidInfo, responsePayload *builderSpec.VersionedSignedBuilderBid, log *logrus.Entry) { + // 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() +} + func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Request, log *logrus.Entry, payload *eth2ApiV1Capella.SignedBlindedBeaconBlock, body []byte) { if payload.Message == nil || payload.Message.Body == nil || payload.Message.Body.ExecutionPayloadHeader == nil { log.WithField("body", string(body)).Error("missing parts of the request payload from the beacon-node") @@ -834,3 +871,12 @@ func (m *BoostService) CheckRelays() int { wg.Wait() return int(numSuccessRequestsToRelay) } + +func (m *BoostService) isPrivilegedRelay(pubkey phase0.BLSPubKey) bool { + for _, builder := range m.privilegedBuilders { + if bytes.Equal(builder[:], pubkey[:]) { + return true + } + } + return false +} diff --git a/server/service_test.go b/server/service_test.go index 47d418aa..f8502d2e 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -72,6 +72,17 @@ func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *te return &backend } +func (be *testBackend) setPrivilegedBuilders(pubKey phase0.BLSPubKey) *testBackend { + privilegedBuilders := make([]phase0.BLSPubKey, 0) + for _, relay := range be.relays { + if bytes.Equal(relay.RelayEntry.PublicKey[:], pubKey[:]) { + privilegedBuilders = append(privilegedBuilders, relay.RelayEntry.PublicKey) + } + } + be.boost.privilegedBuilders = privilegedBuilders + return be +} + func (be *testBackend) request(t *testing.T, method, path string, payload any) *httptest.ResponseRecorder { t.Helper() var req *http.Request @@ -333,7 +344,7 @@ func TestGetHeader(t *testing.T) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionDeneb, ) backend.relays[0].GetHeaderResponse = resp @@ -348,7 +359,7 @@ func TestGetHeader(t *testing.T) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) resp.Capella.Message.Header.BlockHash = nilHash @@ -375,7 +386,7 @@ func TestGetHeader(t *testing.T) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -397,7 +408,7 @@ func TestGetHeader(t *testing.T) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -469,7 +480,7 @@ func TestGetHeaderBids(t *testing.T) { 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -478,7 +489,7 @@ func TestGetHeaderBids(t *testing.T) { 12347, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[1].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -487,7 +498,7 @@ func TestGetHeaderBids(t *testing.T) { 12346, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[2].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -518,7 +529,7 @@ func TestGetHeaderBids(t *testing.T) { 12345, "0xa38385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -526,7 +537,7 @@ func TestGetHeaderBids(t *testing.T) { 12345, "0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[1].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -534,7 +545,7 @@ func TestGetHeaderBids(t *testing.T) { 12345, "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[2].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -570,7 +581,7 @@ func TestGetHeaderBids(t *testing.T) { 12344, "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -593,7 +604,7 @@ func TestGetHeaderBids(t *testing.T) { 12345, "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) @@ -611,6 +622,42 @@ func TestGetHeaderBids(t *testing.T) { require.NoError(t, err) require.Equal(t, uint256.NewInt(12345), value) }) + + t.Run("Use header from a privileged relay", func(t *testing.T) { + backend := newTestBackend(t, 2, time.Second) + backend.setPrivilegedBuilders(backend.relays[0].RelayEntry.PublicKey) + + // privileged relay + backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( + 12348, + "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + backend.relays[0].RelayEntry.PublicKey.String(), + spec.DataVersionCapella, + ) + // non-privileged relay with higher value + backend.relays[1].GetHeaderResponse = backend.relays[1].MakeGetHeaderResponse( + 12349, + "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + backend.relays[1].RelayEntry.PublicKey.String(), + spec.DataVersionCapella, + ) + + rr := backend.request(t, http.MethodGet, path, nil) + + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) + require.Equal(t, 1, backend.relays[1].GetRequestCount(path)) + + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + // Highest value should be 12348, i.e. privileged relay. + resp := new(builderSpec.VersionedSignedBuilderBid) + err := json.Unmarshal(rr.Body.Bytes(), resp) + require.NoError(t, err) + value, err := resp.Value() + require.NoError(t, err) + require.Equal(t, uint256.NewInt(12348), value) + }) } func TestGetPayload(t *testing.T) { @@ -883,7 +930,7 @@ func TestGetPayloadToAllRelays(t *testing.T) { 12345, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + backend.relays[0].RelayEntry.PublicKey.String(), spec.DataVersionCapella, ) rr := backend.request(t, http.MethodGet, getHeaderPath, nil)