Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Min bid #274

Merged
merged 6 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
45 changes: 45 additions & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package cli

import (
"flag"
"math/big"
"os"
"strconv"
"strings"
"time"

"github.com/flashbots/go-boost-utils/types"
"github.com/flashbots/mev-boost/config"
"github.com/flashbots/mev-boost/server"
"github.com/sirupsen/logrus"
Expand All @@ -25,6 +27,7 @@ var (
defaultListenAddr = getEnv("BOOST_LISTEN_ADDR", "localhost:18550")
defaultRelayCheck = os.Getenv("RELAY_STARTUP_CHECK") != ""
defaultGenesisForkVersion = getEnv("GENESIS_FORK_VERSION", "")
defaultRelayMinBidEth = getEnvFloat64("MIN_BID_ETH", 0.001)
Copy link
Collaborator

Choose a reason for hiding this comment

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

One more thing, the default minimum bid should be zero. It's up to the user (operator) to set this value based on their preferences. I'm just concerned with the side effects of this. For example, a default value of 0.001 would affect testnets (like sepolia) where the average mev reward is most likely to be below that threshold. I understand the desire for a non-zero default: low value blocks will be built using the local execution engine. But when CL clients start to compare the value to the block it built locally, I think that would serve as a better "default" minimum.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, that makes sense.

defaultDisableLogVersion = os.Getenv("DISABLE_LOG_VERSION") == "1" // disables adding the version to every log entry

// mev-boost relay request timeouts (see also https://github.com/flashbots/mev-boost/issues/287)
Expand All @@ -46,6 +49,7 @@ var (
listenAddr = flag.String("addr", defaultListenAddr, "listen-address for mev-boost server")
relayURLs = flag.String("relays", "", "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", "", "relay monitor urls - single entry or comma-separated list (scheme://host)")

relayTimeoutMsGetHeader = flag.Int("request-timeout-getheader", defaultTimeoutMsGetHeader, "timeout for getHeader requests to the relay [ms]")
Expand Down Expand Up @@ -161,13 +165,27 @@ func Main() {
}
}

if *relayMinBidEth < 0.0 {
log.Fatal("Please specify a non-negative minimum bid")
}

if *relayMinBidEth > 1000000.0 {
log.Fatal("Minimum bid is too large, please ensure min-bid is denominated in Ethers")
}

relayMinBidWei, err := floatEthTo256Wei(*relayMinBidEth)
if err != nil {
log.WithError(err).Fatal("failed converting min bid")
}

opts := server.BoostServiceOpts{
Log: log,
ListenAddr: *listenAddr,
Relays: relays,
RelayMonitors: relayMonitors,
GenesisForkVersionHex: genesisForkVersionHex,
RelayCheck: *relayCheck,
RelayMinBid: *relayMinBidWei,
RequestTimeoutGetHeader: time.Duration(*relayTimeoutMsGetHeader) * time.Millisecond,
RequestTimeoutGetPayload: time.Duration(*relayTimeoutMsGetPayload) * time.Millisecond,
RequestTimeoutRegVal: time.Duration(*relayTimeoutMsRegVal) * time.Millisecond,
Expand Down Expand Up @@ -201,3 +219,30 @@ func getEnvInt(key string, defaultValue int) int {
}
return defaultValue
}

func getEnvFloat64(key string, defaultValue float64) float64 {
if value, ok := os.LookupEnv(key); ok {
val, err := strconv.ParseFloat(value, 64)
if err == nil {
return val
}
}
return defaultValue
}

// floatEthTo256Wei converts a float (precision 10) denominated in eth to a U256Str denominated in wei
func floatEthTo256Wei(val float64) (*types.U256Str, error) {
allboxes marked this conversation as resolved.
Show resolved Hide resolved
weiU256 := new(types.U256Str)
ethFloat := new(big.Float)
weiFloat := new(big.Float)
weiFloatLessPrecise := new(big.Float)
weiInt := new(big.Int)

ethFloat.SetFloat64(val)
weiFloat.Mul(ethFloat, big.NewFloat(1e18))
weiFloatLessPrecise.SetString(weiFloat.String())
weiFloatLessPrecise.Int(weiInt)

err := weiU256.FromBig(weiInt)
return weiU256, err
}
36 changes: 36 additions & 0 deletions cli/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cli

import (
"math/big"
"testing"

"github.com/flashbots/go-boost-utils/types"
"github.com/stretchr/testify/require"
)

func TestFloatEthTo256Wei(t *testing.T) {
// test with small input
i := 0.000000000000012345
weiU256, err := floatEthTo256Wei(i)
require.NoError(t, err)
allboxes marked this conversation as resolved.
Show resolved Hide resolved
require.Equal(t, types.IntToU256(12345), *weiU256)

// test with zero
i = 0
weiU256, err = floatEthTo256Wei(i)
require.NoError(t, err)
require.Equal(t, types.IntToU256(0), *weiU256)

// test with large input
i = 987654.3
weiU256, err = floatEthTo256Wei(i)
require.NoError(t, err)

r := big.NewInt(9876543)
r.Mul(r, big.NewInt(1e17))
referenceWeiU256 := new(types.U256Str)
err = referenceWeiU256.FromBig(r)
require.NoError(t, err)

require.Equal(t, *refernceWeiU256, *weiU256)
}
10 changes: 9 additions & 1 deletion server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type BoostServiceOpts struct {
RelayMonitors []*url.URL
GenesisForkVersionHex string
RelayCheck bool
RelayMinBid types.U256Str

RequestTimeoutGetHeader time.Duration
RequestTimeoutGetPayload time.Duration
Expand All @@ -63,6 +64,7 @@ type BoostService struct {
log *logrus.Entry
srv *http.Server
relayCheck bool
relayMinBid types.U256Str

builderSigningDomain types.Domain
httpClientGetHeader http.Client
Expand Down Expand Up @@ -90,6 +92,7 @@ func NewBoostService(opts BoostServiceOpts) (*BoostService, error) {
relayMonitors: opts.RelayMonitors,
log: opts.Log,
relayCheck: opts.RelayCheck,
relayMinBid: opts.RelayMinBid,
bids: make(map[bidRespKey]bidResp),

builderSigningDomain: builderSigningDomain,
Expand Down Expand Up @@ -357,9 +360,14 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request)
log.Warn("ignoring bid with 0 value")
return
}

log.Debug("bid received")

// Skip if value (fee) is lower than the minimum bid
if responsePayload.Data.Message.Value.Cmp(&m.relayMinBid) == -1 {
log.Debug("ignoring bid below min-bid value")
return
}

mu.Lock()
defer mu.Unlock()

Expand Down
50 changes: 49 additions & 1 deletion server/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *te
Relays: relayEntries,
GenesisForkVersionHex: "0x00000000",
RelayCheck: true,
RelayMinBid: types.IntToU256(12345),
RequestTimeoutGetHeader: relayTimeout,
RequestTimeoutGetPayload: relayTimeout,
RequestTimeoutRegVal: relayTimeout,
Expand Down Expand Up @@ -75,7 +76,7 @@ func (be *testBackend) request(t *testing.T, method, path string, payload any) *

func TestNewBoostServiceErrors(t *testing.T) {
t.Run("errors when no relays", func(t *testing.T) {
_, err := NewBoostService(BoostServiceOpts{testLog, ":123", []RelayEntry{}, []*url.URL{}, "0x00000000", true, time.Second, time.Second, time.Second})
_, err := NewBoostService(BoostServiceOpts{testLog, ":123", []RelayEntry{}, []*url.URL{}, "0x00000000", true, types.IntToU256(0), time.Second, time.Second, time.Second})
require.Error(t, err)
})
}
Expand Down Expand Up @@ -367,6 +368,53 @@ func TestGetHeader(t *testing.T) {
require.Equal(t, "0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", resp.Data.Message.Header.BlockHash.String())
})

t.Run("Respect minimum bid cutoff", func(t *testing.T) {
// Create backend and register relay.
backend := newTestBackend(t, 1, time.Second)

// Relay will return signed response with value 12344.
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12344,
"0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

// Run the request.
rr := backend.request(t, http.MethodGet, path, nil)

// Each relay must have received the request.
require.Equal(t, 1, backend.relays[0].GetRequestCount(path))

// Request should have no content (min bid is 12345)
require.Equal(t, http.StatusNoContent, rr.Code)
})

t.Run("Allow bids which meet minimum bid cutoff", func(t *testing.T) {
// Create backend and register relay.
backend := newTestBackend(t, 1, time.Second)

// First relay will return signed response with value 12345.
backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse(
12345,
"0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7",
"0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249",
)

// Run the request.
rr := backend.request(t, http.MethodGet, path, nil)

// Each relay must have received the request.
require.Equal(t, 1, backend.relays[0].GetRequestCount(path))

// Value should be 12345 (min bid is 12345)
resp := new(types.GetHeaderResponse)
err := json.Unmarshal(rr.Body.Bytes(), resp)
require.NoError(t, err)
require.Equal(t, types.IntToU256(12345), resp.Data.Message.Value)
})

t.Run("Invalid relay public key", func(t *testing.T) {
backend := newTestBackend(t, 1, time.Second)

Expand Down