Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ Usage of mev-boost:
use Sepolia
-version
only print version
-metrics
enables a metrics server (default: false)
-metrics-addr string
listening address for the metrics server (default: "localhost:18551")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are both flags necessary, or should we only use -metrics-addr and when set then enable, and by default empty string disable?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this would require the user provide a URL/port. Not quite as simple as "enable with -metrics".

I think we did it this way because it mimics how clients do it, eg:

image

```

### `-relays` vs `-relay`
Expand Down Expand Up @@ -309,6 +313,10 @@ Example for setting a minimum bid value of 0.06 ETH:
-relay $YOUR_RELAY_CHOICE_C
```

### Enabling metrics

Optionally, the `-metrics` flag can be provided to expose a prometheus metrics server. The metrics server address/port can be changed with the `-metrics-addr` (e.g., `-metrics-addr localhost:9009`) flag.

---

# API
Expand Down
20 changes: 20 additions & 0 deletions cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
GenesisCategory = "GENESIS"
RelayCategory = "RELAYS"
GeneralCategory = "GENERAL"
Metrics = "METRICS"
)

var flags = []cli.Flag{
Expand Down Expand Up @@ -35,6 +36,10 @@ var flags = []cli.Flag{
timeoutGetPayloadFlag,
timeoutRegValFlag,
maxRetriesFlag,

// metrics
metricsFlag,
metricsAddrFlag,
}

var (
Expand Down Expand Up @@ -178,4 +183,19 @@ var (
Value: 5,
Category: RelayCategory,
}

// metrics
metricsFlag = &cli.BoolFlag{
Name: "metrics",
Sources: cli.EnvVars("METRICS_ENABLED"),
Usage: "enables a metrics server",
Category: Metrics,
}
metricsAddrFlag = &cli.StringFlag{
Name: "metrics-addr",
Sources: cli.EnvVars("METRICS_ADDR"),
Value: "localhost:18551",
Usage: "listening address for the metrics server",
Category: Metrics,
}
Comment on lines +194 to +200
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit: it would be nice to separate it out to metrics-addr and metrics-port into separate flags.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually mirrors how we do it elsewhere. I'd like to keep it like this for consistency.

mev-boost/cli/flags.go

Lines 42 to 48 in 7e47dd2

addrFlag = &cli.StringFlag{
Name: "addr",
Sources: cli.EnvVars("BOOST_LISTEN_ADDR"),
Value: "localhost:18550",
Usage: "listen-address for mev-boost server",
Category: GeneralCategory,
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright sounds good

)
12 changes: 12 additions & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ func start(_ context.Context, cmd *cli.Command) error {
genesisForkVersion, genesisTime = setupGenesis(cmd)
relays, minBid, relayCheck = setupRelays(cmd)
listenAddr = cmd.String(addrFlag.Name)
metricsEnabled = cmd.Bool(metricsFlag.Name)
metricsAddr = cmd.String(metricsAddrFlag.Name)
)

opts := server.BoostServiceOpts{
Expand All @@ -82,6 +84,7 @@ func start(_ context.Context, cmd *cli.Command) error {
RequestTimeoutGetPayload: time.Duration(cmd.Int(timeoutGetPayloadFlag.Name)) * time.Millisecond,
RequestTimeoutRegVal: time.Duration(cmd.Int(timeoutRegValFlag.Name)) * time.Millisecond,
RequestMaxRetries: cmd.Int(maxRetriesFlag.Name),
MetricsAddr: metricsAddr,
}
service, err := server.NewBoostService(opts)
if err != nil {
Expand All @@ -92,6 +95,15 @@ func start(_ context.Context, cmd *cli.Command) error {
log.Error("no relay passed the health-check!")
}

if metricsEnabled {
go func() {
log.Infof("metrics server listening on %v", opts.MetricsAddr)
if err := service.StartMetricsServer(); err != nil {
log.WithError(err).Error("metrics server exited with error")
}
}()
}

log.Infof("listening on %v", listenAddr)
return service.StartHTTPServer()
}
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/flashbots/mev-boost
go 1.24.0

require (
github.com/VictoriaMetrics/metrics v1.40.1
github.com/ethereum/go-ethereum v1.15.9
github.com/flashbots/go-boost-utils v1.9.0
github.com/flashbots/go-utils v0.10.0
Expand Down Expand Up @@ -30,6 +31,8 @@ require (
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/supranational/blst v0.3.14 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
github.com/valyala/histogram v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.13.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/VictoriaMetrics/metrics v1.40.1 h1:FrF5uJRpIVj9fayWcn8xgiI+FYsKGMslzPuOXjdeyR4=
github.com/VictoriaMetrics/metrics v1.40.1/go.mod h1:XE4uudAAIRaJE614Tl5HMrtoEU6+GDZO4QTnNSsZRuA=
github.com/attestantio/go-builder-client v0.7.2 h1:bOrtysEIZd9bEM+mAeT6OtAo6LSAft/qylBLwFoFwZ0=
github.com/attestantio/go-builder-client v0.7.2/go.mod h1:+NADxbaknI5yxl+0mCkMa/VciVsesxRMGNP/poDfV08=
github.com/attestantio/go-eth2-client v0.27.1 h1:g7bm+gG/p+gfzYdEuxuAepVWYb8EO+2KojV5/Lo2BxM=
Expand Down Expand Up @@ -114,6 +116,10 @@ github.com/trailofbits/go-fuzz-utils v0.0.0-20240830175354-474de707d2aa h1:jXdW8
github.com/trailofbits/go-fuzz-utils v0.0.0-20240830175354-474de707d2aa/go.mod h1:/7KgvY5ghyUsjocUh9dMkLCwKtNxqe0kWl5SIdpLtO8=
github.com/urfave/cli/v3 v3.2.0 h1:m8WIXY0U9LCuUl5r+0fqLWDhNYWt6qvlW+GcF4EoXf8=
github.com/urfave/cli/v3 v3.2.0/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
Expand Down
17 changes: 16 additions & 1 deletion server/get_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/flashbots/mev-boost/config"
"github.com/flashbots/mev-boost/server/params"
"github.com/flashbots/mev-boost/server/types"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -94,13 +95,16 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa

// Send the request
log.Debug("requesting header")
start := time.Now()
resp, err := m.httpClientGetHeader.Do(req)
RecordRelayLatency(params.PathGetHeader, relay.String(), float64(time.Since(start).Microseconds()))
if err != nil {
log.WithError(err).Warn("error calling getHeader on relay")
return
}
defer resp.Body.Close()

RecordRelayStatusCode(strconv.Itoa(resp.StatusCode), params.PathGetHeader, relay.String())
// Check if no header is available
if resp.StatusCode == http.StatusNoContent {
log.Debug("no-content response")
Expand Down Expand Up @@ -207,9 +211,15 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa

log.Debug("bid received")

RecordRelayLastSlot(relay.String(), uint64(slot))

valueEthFloat64, _ := valueEth.Float64()
RecordBidValue(relay.String(), valueEthFloat64)

// Skip if value is lower than the minimum bid
if bidInfo.value.CmpBig(m.relayMinBid.BigInt()) == -1 {
log.Debug("ignoring bid below min-bid value")
IncrementBidBelowMinBid(relay.String())
return
}

Expand Down Expand Up @@ -248,13 +258,18 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa
log.Debug("new best bid")
result.response = *bid
result.bidInfo = bidInfo

result.t = time.Now()
}(relay)
}
wg.Wait()

// Set the winning relays before returning
result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())]

if len(result.relays) > 0 {
RecordWinningBidValue(result.bidInfo.value.Float64())
}

return result, nil
}

Expand Down
18 changes: 11 additions & 7 deletions server/get_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"mime"
"net/http"
"slices"
"strconv"
"sync/atomic"
"time"

Expand Down Expand Up @@ -212,20 +213,23 @@ func (m *BoostService) innerGetPayload(log *logrus.Entry, signedBlindedBeaconBlo
req.Header.Set(HeaderDateMilliseconds, fmt.Sprintf("%d", time.Now().UTC().UnixMilli()))
req.Header.Set(HeaderUserAgent, userAgent)

// Send the request
statusCode := http.StatusOK
endpoint := params.PathGetPayload
if version == GetPayloadV2 {
statusCode = http.StatusAccepted
endpoint = params.PathGetPayloadV2
}
// Send the request and record latency
log.Debug("submitting signed blinded block")
start := time.Now()
resp, err := m.httpClientGetPayload.Do(req)
RecordRelayLatency(endpoint, relay.String(), float64(time.Since(start).Microseconds()))
if err != nil {
log.WithError(err).Warnf("error calling getPayload%s on relay", version)
return nil, err
}

var statusCode int
if version == GetPayloadV1 {
statusCode = http.StatusOK
} else {
statusCode = http.StatusAccepted
}
RecordRelayStatusCode(strconv.Itoa(statusCode), endpoint, relay.String())
// Check that the response was successful
if resp.StatusCode != statusCode {
err = fmt.Errorf("%w: %d", errHTTPErrorResponse, resp.StatusCode)
Expand Down
58 changes: 58 additions & 0 deletions server/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package server

import (
"fmt"

"github.com/VictoriaMetrics/metrics"
)

var winningBidValue = metrics.NewHistogram("mev_boost_winning_bid_value")

const (
beaconNodeStatusLabel = `mev_boost_beacon_node_status_code_total{http_status_code="%s",endpoint="%s"}`
bidValuesLabel = `mev_boost_bid_values{relay="%s"}`
bidsBelowMinBidLabel = `mev_boost_bids_below_min_bid_total{relay="%s"}`
relayLatencyLabel = `mev_boost_relay_latency{endpoint="%s",relay="%s"}`
relayStatusCodeLabel = `mev_boost_relay_status_code_total{http_status_code="%s",endpoint="%s",relay="%s"}`
relayLastSlotLabel = `mev_boost_relay_last_slot{relay="%s"}`
msIntoSlotLabel = `mev_boost_millisec_into_slot{endpoint="%s"}`
)

func IncrementBeaconNodeStatus(status, endpoint string) {
l := fmt.Sprintf(beaconNodeStatusLabel, status, endpoint)
metrics.GetOrCreateCounter(l).Inc()
}

func RecordBidValue(relay string, value float64) {
l := fmt.Sprintf(bidValuesLabel, relay)
metrics.GetOrCreateHistogram(l).Update(value)
}

func IncrementBidBelowMinBid(relay string) {
l := fmt.Sprintf(bidsBelowMinBidLabel, relay)
metrics.GetOrCreateCounter(l).Inc()
}

func RecordWinningBidValue(value float64) {
winningBidValue.Update(value)
}

func RecordRelayLatency(endpoint, relay string, latency float64) {
l := fmt.Sprintf(relayLatencyLabel, endpoint, relay)
metrics.GetOrCreateHistogram(l).Update(latency)
}

func RecordRelayStatusCode(httpStatus, endpoint, relay string) {
l := fmt.Sprintf(relayStatusCodeLabel, httpStatus, endpoint, relay)
metrics.GetOrCreateCounter(l).Inc()
}

func RecordRelayLastSlot(relay string, slot uint64) {
l := fmt.Sprintf(relayLastSlotLabel, relay)
metrics.GetOrCreateGauge(l, nil).Set(float64(slot))
}

func RecordMsIntoSlot(endpoint string, ms float64) {
l := fmt.Sprintf(msIntoSlotLabel, endpoint)
metrics.GetOrCreateHistogram(l).Update(ms)
}
5 changes: 5 additions & 0 deletions server/register_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"context"
"fmt"
"net/http"
"strconv"
"time"

"github.com/flashbots/mev-boost/server/params"
"github.com/flashbots/mev-boost/server/types"
Expand Down Expand Up @@ -45,14 +47,17 @@ func (m *BoostService) registerValidator(log *logrus.Entry, regBytes []byte, hea
}).Debug("sending the registerValidator request")

// Send the request
start := time.Now()
resp, err := m.httpClientRegVal.Do(req)
RecordRelayLatency(params.PathRegisterValidator, relay.String(), float64(time.Since(start).Microseconds()))
if err != nil {
log.WithError(err).Warn("error calling registerValidator on relay")
respErrCh <- err
return
}
resp.Body.Close()

RecordRelayStatusCode(strconv.Itoa(resp.StatusCode), params.PathRegisterValidator, relay.String())
// Check if response is successful
if resp.StatusCode == http.StatusOK {
log.Debug("relay accepted registrations")
Expand Down
Loading
Loading