diff --git a/common/test_utils.go b/common/test_utils.go index 1657cf6d..cc77dae0 100644 --- a/common/test_utils.go +++ b/common/test_utils.go @@ -4,6 +4,7 @@ import ( "bytes" "compress/gzip" "encoding/base64" + "encoding/hex" "encoding/json" "io" "os" @@ -12,6 +13,7 @@ import ( "github.com/attestantio/go-builder-client/api/capella" "github.com/attestantio/go-eth2-client/spec/bellatrix" consensuscapella "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/flashbots/go-boost-utils/bls" boostTypes "github.com/flashbots/go-boost-utils/types" "github.com/sirupsen/logrus" @@ -81,6 +83,32 @@ func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2) BuilderSu } } +func TestBuilderSubmitBlockRequestV2(sk *bls.SecretKey, bid *BidTraceV2) *SubmitBlockRequestV2Optimistic { + signature, err := boostTypes.SignMessage(bid, boostTypes.DomainBuilder, sk) + check(err, " SignMessage: ", bid, sk) + + wRoot, err := hex.DecodeString("792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535") + check(err) + return &SubmitBlockRequestV2Optimistic{ + Message: &bid.BidTrace, + ExecutionPayloadHeader: &consensuscapella.ExecutionPayloadHeader{ //nolint:exhaustruct + TransactionsRoot: [32]byte{}, + Timestamp: bid.Slot * 12, // 12 seconds per slot. + PrevRandao: _HexToHash("01234567890123456789012345678901"), + WithdrawalsRoot: phase0.Root(wRoot), + ExtraData: []byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }, + }, + Signature: [96]byte(signature), + Transactions: []bellatrix.Transaction{[]byte{0x03}}, + Withdrawals: []*consensuscapella.Withdrawal{}, + } +} + func LoadGzippedBytes(t *testing.T, filename string) []byte { t.Helper() fi, err := os.Open(filename) diff --git a/common/types_spec.go b/common/types_spec.go index e4adc9b2..8e8fb775 100644 --- a/common/types_spec.go +++ b/common/types_spec.go @@ -14,6 +14,7 @@ import ( utilcapella "github.com/attestantio/go-eth2-client/util/capella" "github.com/flashbots/go-boost-utils/bls" boostTypes "github.com/flashbots/go-boost-utils/types" + "github.com/holiman/uint256" ) var ( @@ -71,6 +72,33 @@ func BuildGetHeaderResponse(payload *BuilderSubmitBlockRequest, sk *bls.SecretKe return nil, ErrEmptyPayload } +func BuildGetHeaderResponseHeaderOnly(value *uint256.Int, header *consensuscapella.ExecutionPayloadHeader, sk *bls.SecretKey, pubkey *boostTypes.PublicKey, domain boostTypes.Domain) (*GetHeaderResponse, error) { + builderBid := capella.BuilderBid{ + Value: value, + Header: header, + Pubkey: *(*phase0.BLSPubKey)(pubkey), + } + + sig, err := boostTypes.SignMessage(&builderBid, domain, sk) + if err != nil { + return nil, err + } + + signedBuilderBid := &capella.SignedBuilderBid{ + Message: &builderBid, + Signature: phase0.BLSSignature(sig), + } + + return &GetHeaderResponse{ + Capella: &spec.VersionedSignedBuilderBid{ + Version: consensusspec.DataVersionCapella, + Capella: signedBuilderBid, + Bellatrix: nil, + }, + Bellatrix: nil, + }, nil +} + func BuildGetPayloadResponse(payload *BuilderSubmitBlockRequest) (*GetPayloadResponse, error) { if payload.Bellatrix != nil { return &GetPayloadResponse{ diff --git a/datastore/redis.go b/datastore/redis.go index c9c3eef1..23e2f3c7 100644 --- a/datastore/redis.go +++ b/datastore/redis.go @@ -497,10 +497,12 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis // // Time to save things in Redis // - // 1. Save the execution payload - err = r.SaveExecutionPayloadCapella(ctx, pipeliner, payload.Slot(), payload.ProposerPubkey(), payload.BlockHash(), getPayloadResponse.Capella.Capella) - if err != nil { - return state, err + // 1. Save the execution payload (only if it was passed in). + if getPayloadResponse != nil { + err = r.SaveExecutionPayloadCapella(ctx, pipeliner, payload.Slot(), payload.ProposerPubkey(), payload.BlockHash(), getPayloadResponse.Capella.Capella) + if err != nil { + return state, err + } } // Record time needed to save payload @@ -513,7 +515,6 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis if err != nil { return state, err } - state.WasBidSaved = true builderBids.bidValues[payload.BuilderPubkey().String()] = payload.Value() // Record time needed to save bid @@ -543,6 +544,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis return state, err } state.IsNewTopBid = payload.Value().Cmp(state.TopBidValue) == 0 + // An Exec happens in _updateTopBid. + state.WasBidSaved = true // Record time needed to update top bid nextTime = time.Now().UTC() diff --git a/go.mod b/go.mod index 48d3f6a5..461c6280 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 github.com/btcsuite/btcd/btcutil v1.1.2 github.com/buger/jsonparser v1.1.1 - github.com/ethereum/go-ethereum v1.12.2 + github.com/ethereum/go-ethereum v1.13.1 github.com/flashbots/go-boost-utils v1.6.0 - github.com/flashbots/go-utils v0.4.11 + github.com/flashbots/go-utils v0.5.0 github.com/go-redis/redis/v9 v9.0.0-rc.1 github.com/gorilla/mux v1.8.0 github.com/holiman/uint256 v1.2.3 @@ -35,10 +35,12 @@ require ( github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect + github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.11.0 // indirect + github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/ethereum/c-kzg-4844 v0.3.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect @@ -46,7 +48,7 @@ require ( github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -60,13 +62,15 @@ require ( github.com/prometheus/procfs v0.9.0 // indirect github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/supranational/blst v0.3.11 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/protobuf v1.28.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) require ( - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/btcsuite/btcd v0.23.0 // indirect @@ -74,10 +78,10 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/ferranbt/fastssz v0.1.3 - github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -93,14 +97,14 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tdewolff/parse v2.3.4+incompatible // indirect github.com/tdewolff/test v1.0.7 // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/yuin/gopher-lua v1.1.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.23.0 // indirect - golang.org/x/crypto v0.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.9.0 // indirect + golang.org/x/sys v0.12.0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 71fb0994..4731e53b 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKz github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -27,7 +27,7 @@ github.com/attestantio/go-builder-client v0.3.0/go.mod h1:DwesMTOqnCp4u+n3uZ+fWL github.com/attestantio/go-eth2-client v0.16.4 h1:utfGx49JZN4W7IT6/txdwE9qobP3b/eTa3Ge1PJFRJg= github.com/attestantio/go-eth2-client v0.16.4/go.mod h1:lgxKsjRSxQnHSWxSbtZLGYlFkE3vFM4L+DK4bXqAxAU= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= @@ -69,15 +69,15 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= +github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= +github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= @@ -90,16 +90,18 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= +github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -112,8 +114,10 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= -github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= +github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= +github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= +github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= @@ -122,8 +126,8 @@ github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16M github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= github.com/flashbots/go-boost-utils v1.6.0 h1:XIcPDG6q0Tesh3kcCXyv61ncD/WVYOMCO2OVzPznvMA= github.com/flashbots/go-boost-utils v1.6.0/go.mod h1:fL9Jc738zFENkbn5HNnVzIfuRcvCO1djlT/ylyT34Zw= -github.com/flashbots/go-utils v0.4.11 h1:rIyjdPZNit3VyWhQPig9eqCw0N1qLXBPVGZdkrA8kRw= -github.com/flashbots/go-utils v0.4.11/go.mod h1:fDuVDYOlnBX1n5U1JfuGSdYnt2obx0yTgTbk7Tjwa+Q= +github.com/flashbots/go-utils v0.5.0 h1:ldjWta9B9//DJU2QcwRbErez3+1aKhSn6EoFc6d5kPY= +github.com/flashbots/go-utils v0.5.0/go.mod h1:LauDwifaRdSK0mS5X34GR59pJtUu1T/lOFNdff1BqtI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -139,8 +143,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= @@ -199,8 +203,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -382,6 +386,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo= @@ -390,10 +396,10 @@ github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxp github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM= github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c h1:4WU+p200eLYtBsx3M5CKXvkjVdf5SC3W9nMg37y0TFI= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -419,14 +425,13 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -436,8 +441,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= @@ -482,6 +487,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -492,6 +498,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -501,7 +508,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -516,10 +522,12 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/services/api/optimistic_test.go b/services/api/optimistic_test.go index b0f6e491..2224944a 100644 --- a/services/api/optimistic_test.go +++ b/services/api/optimistic_test.go @@ -59,7 +59,7 @@ type blockRequestOpts struct { domain boostTypes.Domain } -func startTestBackend(t *testing.T) (*phase0.BLSPubKey, *bls.SecretKey, *testBackend) { +func generateKeyPair(t *testing.T) (*phase0.BLSPubKey, *bls.SecretKey) { t.Helper() // Setup test key pair. sk, _, err := bls.GenerateNewKeypair() @@ -69,6 +69,11 @@ func startTestBackend(t *testing.T) (*phase0.BLSPubKey, *bls.SecretKey, *testBac pkBytes := blsPubkey.Bytes() var pubkey phase0.BLSPubKey copy(pubkey[:], pkBytes[:]) + return &pubkey, sk +} + +func startTestBackend(t *testing.T, pubkey *phase0.BLSPubKey) *testBackend { + t.Helper() pkStr := pubkey.String() // Setup test backend. @@ -134,7 +139,7 @@ func startTestBackend(t *testing.T) (*phase0.BLSPubKey, *bls.SecretKey, *testBac // require.Equal(t, count, 1) backend.relay.headSlot.Store(40) - return &pubkey, sk, backend + return backend } func runOptimisticBlockSubmission(t *testing.T, opts blockRequestOpts, simErr error, backend *testBackend) *httptest.ResponseRecorder { @@ -176,7 +181,8 @@ func TestSimulateBlock(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - pubkey, secretkey, backend := startTestBackend(t) + pubkey, secretkey := generateKeyPair(t) + backend := startTestBackend(t, pubkey) backend.relay.blockSimRateLimiter = &MockBlockSimulationRateLimiter{ simulationError: tc.simulationError, } @@ -224,7 +230,8 @@ func TestProcessOptimisticBlock(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - pubkey, secretkey, backend := startTestBackend(t) + pubkey, secretkey := generateKeyPair(t) + backend := startTestBackend(t, pubkey) pkStr := pubkey.String() backend.relay.blockSimRateLimiter = &MockBlockSimulationRateLimiter{ simulationError: tc.simulationError, @@ -273,7 +280,8 @@ func TestDemoteBuilder(t *testing.T) { IsOptimistic: false, IsHighPrio: true, } - pubkey, secretkey, backend := startTestBackend(t) + pubkey, secretkey := generateKeyPair(t) + backend := startTestBackend(t, pubkey) pkStr := pubkey.String() req := common.TestBuilderSubmitBlockRequest(secretkey, getTestBidTrace(*pubkey, collateral)) backend.relay.demoteBuilder(pkStr, &req, errFake) @@ -291,7 +299,8 @@ func TestDemoteBuilder(t *testing.T) { } func TestPrepareBuildersForSlot(t *testing.T) { - pubkey, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) pkStr := pubkey.String() // Clear cache. backend.relay.blockBuildersCache = map[string]*blockBuilderCacheEntry{} @@ -350,7 +359,8 @@ func TestBuilderApiSubmitNewBlockOptimistic(t *testing.T) { for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { - pubkey, secretkey, backend := startTestBackend(t) + pubkey, secretkey := generateKeyPair(t) + backend := startTestBackend(t, pubkey) backend.relay.optimisticSlot.Store(slot) backend.relay.capellaEpoch = 1 var randaoHash boostTypes.Hash @@ -392,7 +402,8 @@ func TestBuilderApiSubmitNewBlockOptimistic(t *testing.T) { } func TestInternalBuilderStatus(t *testing.T) { - pubkey, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) // Set all to false initially. err := backend.relay.db.SetBlockBuilderStatus(pubkey.String(), common.BuilderStatus{}) require.NoError(t, err) @@ -419,7 +430,8 @@ func TestInternalBuilderStatus(t *testing.T) { } func TestInternalBuilderCollateral(t *testing.T) { - pubkey, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) path := "/internal/v1/builder/collateral/" + pubkey.String() // Set & Get. @@ -434,3 +446,200 @@ func TestInternalBuilderCollateral(t *testing.T) { require.Equal(t, resp.BuilderID, "builder0x69") require.Equal(t, resp.Collateral, "10000") } + +func TestBuilderApiSubmitNewBlockOptimisticV2_fail_cancellations(t *testing.T) { + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) + outBytes := make([]byte, 10) + + // Disable cancellations. + backend.relay.ffEnableCancellations = false + + // Set request with cancellations true. + rr := backend.requestBytes(http.MethodPost, pathSubmitNewBlockV2+"?cancellations=1", outBytes, map[string]string{}) + + // Check bad request is returned. + require.Equal(t, rr.Code, 400) +} + +func TestBuilderApiSubmitNewBlockOptimisticV2_fail_gzip(t *testing.T) { + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) + outBytes := make([]byte, 10) + + // Set request with gzip. + rr := backend.requestBytes(http.MethodPost, pathSubmitNewBlockV2, outBytes, map[string]string{"Content-Encoding": "gzip"}) + + // Check bad request is returned. + require.Equal(t, rr.Code, 400) +} + +func TestBuilderApiSubmitNewBlockOptimisticV2_fail_read_header(t *testing.T) { + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) + outBytes := make([]byte, 0) // 0 bytes. + + // Valid request but no bytes. + rr := backend.requestBytes(http.MethodPost, pathSubmitNewBlockV2, outBytes, map[string]string{}) + + // Check bad request is returned. + require.Equal(t, rr.Code, 400) +} + +func TestBuilderApiSubmitNewBlockOptimisticV2_fail_ssz_decode_header(t *testing.T) { + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) + outBytes := make([]byte, 944) // 944 bytes is min required to try ssz decoding. + outBytes[0] = 0xaa + + // Valid request but no bytes. + rr := backend.requestBytes(http.MethodPost, pathSubmitNewBlockV2, outBytes, map[string]string{}) + + // Check bad request is returned. + require.Equal(t, rr.Code, 400) +} + +func TestBuilderApiSubmitNewBlockOptimisticV2_full(t *testing.T) { + pubkey, secretkey := generateKeyPair(t) + + // Construct our test requests. + cleanReq := common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)) + badSigReq := common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)) + badSigReq.Signature[0] = 0xaa + invalidSigReq := common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)) + // Sign over a message with a different value. + diffSig, err := boostTypes.SignMessage(getTestBidTrace(*pubkey, 11), boostTypes.DomainBuilder, secretkey) + require.Nil(t, err) + invalidSigReq.Signature = phase0.BLSSignature(diffSig) + badTimestampReq := common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)) + badTimestampReq.ExecutionPayloadHeader.Timestamp -= 1 + badWithdrawalsRootReq := common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)) + badWithdrawalsRootReq.ExecutionPayloadHeader.WithdrawalsRoot[0] = 0xaa + + // Bad requests that need signatures. + bidBadFeeRecipient := getTestBidTrace(*pubkey, 10) + bidBadFeeRecipient.ProposerFeeRecipient[0] = 0x42 + badFeeRecipient := common.TestBuilderSubmitBlockRequestV2(secretkey, bidBadFeeRecipient) + + testCases := []struct { + description string + httpCode uint64 + simError error + overwriteEntry bool + entry *blockBuilderCacheEntry + request *common.SubmitBlockRequestV2Optimistic + }{ + { + description: "success", + httpCode: 200, // success + request: cleanReq, + }, + { + description: "failure_malformed_signature", + httpCode: 400, // failure + request: badSigReq, + }, + { + description: "failure_invalid_signature", + httpCode: 400, // failure + request: invalidSigReq, + }, + { + description: "failure_no_builder_entry", + httpCode: 400, // failure + request: common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)), + entry: nil, + overwriteEntry: true, + }, + { + description: "failure_builder_not_optimistic", + httpCode: 400, // failure + request: common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)), + entry: &blockBuilderCacheEntry{ + status: common.BuilderStatus{ + IsOptimistic: false, + }, + }, + overwriteEntry: true, + }, + { + description: "failure_builder_insufficient_collateral", + httpCode: 400, // failure + request: common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)), + entry: &blockBuilderCacheEntry{ + status: common.BuilderStatus{ + IsOptimistic: true, + }, + collateral: big.NewInt(int64(9)), + }, + overwriteEntry: true, + }, + { + description: "failure_builder_blacklisted", + httpCode: 200, // we return 200 here. + request: common.TestBuilderSubmitBlockRequestV2(secretkey, getTestBidTrace(*pubkey, 10)), + entry: &blockBuilderCacheEntry{ + status: common.BuilderStatus{ + IsOptimistic: true, + IsBlacklisted: true, + }, + collateral: big.NewInt(int64(collateral)), + }, + overwriteEntry: true, + }, + { + description: "failure_bad_time_stamp", + httpCode: 400, // failure + request: badTimestampReq, + }, + { + description: "failure_bad_fee_recipient", + httpCode: 400, // failure + request: badFeeRecipient, + }, + { + description: "failure_bad_withdrawals_root", + httpCode: 400, // failure + request: badWithdrawalsRootReq, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + backend := startTestBackend(t, pubkey) + backend.relay.optimisticSlot.Store(slot) + backend.relay.capellaEpoch = 1 + var randaoHash boostTypes.Hash + err := randaoHash.FromSlice([]byte(randao)) + require.NoError(t, err) + withRoot, err := ComputeWithdrawalsRoot([]*consensuscapella.Withdrawal{}) + require.NoError(t, err) + backend.relay.payloadAttributes[emptyHash] = payloadAttributesHelper{ + slot: slot, + withdrawalsRoot: withRoot, + payloadAttributes: beaconclient.PayloadAttributes{ + PrevRandao: randaoHash.String(), + }, + } + + if tc.overwriteEntry { + if tc.entry == nil { + delete(backend.relay.blockBuildersCache, pubkey.String()) + } else { + backend.relay.blockBuildersCache[pubkey.String()] = tc.entry + } + } + + backend.relay.blockSimRateLimiter = &MockBlockSimulationRateLimiter{ + simulationError: tc.simError, + } + + outBytes, err := tc.request.MarshalSSZ() + require.NoError(t, err) + + // Check http code. + rr := backend.requestBytes(http.MethodPost, pathSubmitNewBlockV2, outBytes, map[string]string{}) + require.Equal(t, uint64(rr.Code), tc.httpCode) + }) + } +} diff --git a/services/api/service.go b/services/api/service.go index e4e29e26..806f1f7a 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -23,6 +23,7 @@ import ( "github.com/NYTimes/gziphandler" builderCapella "github.com/attestantio/go-builder-client/api/capella" "github.com/attestantio/go-eth2-client/api/v1/capella" + capellaSpec "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/buger/jsonparser" "github.com/flashbots/go-boost-utils/bls" @@ -53,8 +54,6 @@ var ( ErrRelayPubkeyMismatch = errors.New("relay pubkey does not match existing one") ErrServerAlreadyStarted = errors.New("server was already started") ErrBuilderAPIWithoutSecretKey = errors.New("cannot start builder API without secret key") - ErrMismatchedForkVersions = errors.New("can not find matching fork versions as retrieved from beacon node") - ErrMissingForkVersions = errors.New("invalid fork version from beacon node") ) var ( @@ -67,6 +66,7 @@ var ( // Block builder API pathBuilderGetValidators = "/relay/v1/builder/validators" pathSubmitNewBlock = "/relay/v1/builder/blocks" + pathSubmitNewBlockV2 = "/relay/v2/builder/blocks" // Data API pathDataProposerPayloadDelivered = "/relay/v1/data/bidtraces/proposer_payload_delivered" @@ -344,6 +344,7 @@ func (api *RelayAPI) getRouter() http.Handler { api.log.Info("block builder API enabled") r.HandleFunc(pathBuilderGetValidators, api.handleBuilderGetValidators).Methods(http.MethodGet) r.HandleFunc(pathSubmitNewBlock, api.handleSubmitNewBlock).Methods(http.MethodPost) + r.HandleFunc(pathSubmitNewBlockV2, api.handleSubmitNewBlockV2).Methods(http.MethodPost) } // Data API @@ -408,6 +409,7 @@ func (api *RelayAPI) StartServer() (err error) { if err != nil { return err } + var foundCapellaEpoch, foundDenebEpoch bool for _, fork := range forkSchedule.Data { log.Infof("forkSchedule: version=%s / epoch=%d", fork.CurrentVersion, fork.Epoch) @@ -421,14 +423,10 @@ func (api *RelayAPI) StartServer() (err error) { } } - if !foundCapellaEpoch || !foundDenebEpoch { - return ErrMissingForkVersions - } - // Print fork version information - if hasReachedFork(currentSlot, api.denebEpoch) { + if foundDenebEpoch && hasReachedFork(currentSlot, api.denebEpoch) { log.Infof("deneb fork detected (currentEpoch: %d / denebEpoch: %d)", common.SlotToEpoch(currentSlot), api.denebEpoch) - } else if hasReachedFork(currentSlot, api.capellaEpoch) { + } else if foundCapellaEpoch && hasReachedFork(currentSlot, api.capellaEpoch) { log.Infof("capella fork detected (currentEpoch: %d / capellaEpoch: %d)", common.SlotToEpoch(currentSlot), api.capellaEpoch) } @@ -1508,7 +1506,7 @@ func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *log return slotDuty.Entry.Message.GasLimit, true } -func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *logrus.Entry, payload *common.BuilderSubmitBlockRequest) bool { +func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *logrus.Entry, payload *common.BuilderSubmitBlockRequest, withdrawalsRoot phase0.Root) bool { api.payloadAttributesLock.RLock() attrs, ok := api.payloadAttributes[payload.ParentHash()] api.payloadAttributesLock.RUnlock() @@ -1526,13 +1524,6 @@ func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *log } if hasReachedFork(payload.Slot(), api.capellaEpoch) { // Capella requires correct withdrawals - withdrawalsRoot, err := ComputeWithdrawalsRoot(payload.Withdrawals()) - if err != nil { - log.WithError(err).Warn("could not compute withdrawals root from payload") - api.RespondError(w, http.StatusBadRequest, "could not compute withdrawals root") - return false - } - if withdrawalsRoot != attrs.withdrawalsRoot { msg := fmt.Sprintf("incorrect withdrawals root - got: %s, expected: %s", withdrawalsRoot.String(), attrs.withdrawalsRoot.String()) log.Info(msg) @@ -1610,7 +1601,7 @@ type bidFloorOpts struct { payload *common.BuilderSubmitBlockRequest } -func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, *logrus.Entry, bool) { +func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, bool) { // Reject new submissions once the payload for this slot was delivered - TODO: store in memory as well slotLastPayloadDelivered, err := api.redis.GetLastSlotDelivered(context.Background(), opts.tx) if err != nil && !errors.Is(err, redis.Nil) { @@ -1618,7 +1609,7 @@ func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, *logrus.En } else if opts.payload.Slot() <= slotLastPayloadDelivered { opts.log.Info("rejecting submission because payload for this slot was already delivered") api.RespondError(opts.w, http.StatusBadRequest, "payload for this slot was already delivered") - return nil, nil, false + return nil, false } // Grab floor bid value @@ -1641,17 +1632,17 @@ func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, *logrus.En if err != nil { opts.log.WithError(err).Error("failed processing cancellable bid below floor") api.RespondError(opts.w, http.StatusInternalServerError, "failed processing cancellable bid below floor") - return nil, nil, false + return nil, false } api.Respond(opts.w, http.StatusAccepted, "accepted bid below floor, skipped validation") - return nil, nil, false + return nil, false } else if !opts.cancellationsEnabled && isBidAtOrBelowFloor { // without cancellations: if at or below floor -> ignore opts.simResultC <- &blockSimResult{false, false, nil, nil} opts.log.Info("submission at or below floor bid value, without cancellation") api.RespondMsg(opts.w, http.StatusAccepted, "accepted bid below floor, skipped validation") - return nil, nil, false + return nil, false } - return floorBidValue, opts.log, true + return floorBidValue, true } type redisUpdateBidOpts struct { @@ -1844,7 +1835,13 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque log = log.WithField("timestampBeforeAttributesCheck", time.Now().UTC().UnixMilli()) - ok = api.checkSubmissionPayloadAttrs(w, log, payload) + withdrawalsRoot, err := ComputeWithdrawalsRoot(payload.Withdrawals()) + if err != nil { + log.WithError(err).Warn("could not compute withdrawals root from payload") + api.RespondError(w, http.StatusBadRequest, "could not compute withdrawals root") + return + } + ok = api.checkSubmissionPayloadAttrs(w, log, payload, withdrawalsRoot) if !ok { return } @@ -1879,7 +1876,7 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque simResultC: simResultC, payload: payload, } - floorBidValue, log, ok := api.checkFloorBidValue(bfOpts) + floorBidValue, ok := api.checkFloorBidValue(bfOpts) if !ok { return } @@ -2059,6 +2056,298 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque w.WriteHeader(http.StatusOK) } +func (api *RelayAPI) handleSubmitNewBlockV2(w http.ResponseWriter, req *http.Request) { + var pf common.Profile + var prevTime, nextTime time.Time + + headSlot := api.headSlot.Load() + receivedAt := time.Now().UTC() + prevTime = receivedAt + + args := req.URL.Query() + isCancellationEnabled := args.Get("cancellations") == "1" + + log := api.log.WithFields(logrus.Fields{ + "method": "submitNewBlockV2", + "contentLength": req.ContentLength, + "headSlot": headSlot, + "cancellationEnabled": isCancellationEnabled, + "timestampRequestStart": receivedAt.UnixMilli(), + }) + + // Log at start and end of request + log.Info("request initiated") + defer func() { + log.WithFields(logrus.Fields{ + "timestampRequestFin": time.Now().UTC().UnixMilli(), + "requestDurationMs": time.Since(receivedAt).Milliseconds(), + }).Info("request finished") + }() + + // If cancellations are disabled but builder requested it, return error + if isCancellationEnabled && !api.ffEnableCancellations { + log.Info("builder submitted with cancellations enabled, but feature flag is disabled") + api.RespondError(w, http.StatusBadRequest, "cancellations are disabled") + return + } + + var err error + var r io.Reader = req.Body + if req.Header.Get("Content-Encoding") == "gzip" { + r, err = gzip.NewReader(req.Body) + if err != nil { + log.WithError(err).Warn("could not create gzip reader") + api.RespondError(w, http.StatusBadRequest, err.Error()) + return + } + log = log.WithField("gzip-req", true) + } + + var buf bytes.Buffer + rHeader := io.TeeReader(r, &buf) + + // Header at most 944 bytes. + headBuf := make([]byte, 944) + + // Read header bytes. + _, err = io.ReadFull(rHeader, headBuf) + if err != nil { + log.WithError(err).Warn("could not read full header") + api.RespondError(w, http.StatusBadRequest, err.Error()) + return + } + + // Unmarshall just header. + var header common.SubmitBlockRequestV2Optimistic + err = header.UnmarshalSSZHeaderOnly(headBuf) + if err != nil { + log.WithError(err).Warn("could not unmarshall request") + api.RespondError(w, http.StatusBadRequest, err.Error()) + return + } + + nextTime = time.Now().UTC() + pf.Decode = uint64(nextTime.Sub(prevTime).Microseconds()) + prevTime = nextTime + + bid := header.Message + sig := header.Signature + eph := header.ExecutionPayloadHeader + + log = log.WithFields(logrus.Fields{ + "timestampAfterDecoding": time.Now().UTC().UnixMilli(), + "slot": bid.Slot, + "builderPubkey": bid.BuilderPubkey.String(), + "blockHash": bid.BlockHash.String(), + "proposerPubkey": bid.ProposerPubkey.String(), + "parentHash": bid.ParentHash.String(), + "value": bid.Value.String(), + }) + + log.WithFields(logrus.Fields{ + "bid": bid, + "signature": sig, + "decode_time": pf.Decode, + }).Info("optimistically parsed bid") + + // Check optimistic eligibility. + builderPubkey := bid.BuilderPubkey + builderEntry, ok := api.blockBuildersCache[builderPubkey.String()] + if !ok { + log.Errorf("unable to read builder: %x from the builder cache, rejecting submission.", builderPubkey.String()) + api.RespondError(w, http.StatusBadRequest, "unknown builder pubkey") + return + } + if !builderEntry.status.IsOptimistic { + log.Errorf("builder: %x not eligible for optimistic relaying.", builderPubkey.String()) + api.RespondError(w, http.StatusBadRequest, "builder not eligible for optimistic relaying") + return + } + if builderEntry.collateral.Cmp(bid.Value.ToBig()) <= 0 || bid.Slot != api.optimisticSlot.Load() { + log.Warningf("insufficient collateral or non-optimistic slot. reverting to standard relaying.") + api.RespondError(w, http.StatusBadRequest, "insufficient collateral, unable to execute v2 optimistic relaying") + return + } + log = log.WithFields(logrus.Fields{ + "builderEntry": builderEntry, + }) + + // Construct request from header. + payload := &common.BuilderSubmitBlockRequest{ //nolint:exhaustruct + Capella: &builderCapella.SubmitBlockRequest{ + Message: bid, + // Transactions and Withdrawals are intentionally omitted. + ExecutionPayload: &capellaSpec.ExecutionPayload{ //nolint:exhaustruct + ParentHash: eph.ParentHash, + FeeRecipient: eph.FeeRecipient, + StateRoot: eph.StateRoot, + ReceiptsRoot: eph.ReceiptsRoot, + LogsBloom: eph.LogsBloom, + PrevRandao: eph.PrevRandao, + BlockNumber: eph.BlockNumber, + GasLimit: eph.GasLimit, + GasUsed: eph.GasUsed, + Timestamp: eph.Timestamp, + ExtraData: eph.ExtraData, + BaseFeePerGas: eph.BaseFeePerGas, + BlockHash: eph.BlockHash, + }, + Signature: sig, + }, + } + + ok = api.checkSubmissionSlotDetails(w, log, headSlot, payload) + if !ok { + return + } + + _, ok = api.checkBuilderEntry(w, log, builderPubkey) + if !ok { + return + } + + log = log.WithFields(logrus.Fields{ + "builderIsHighPrio": builderEntry.status.IsHighPrio, + "timestampAfterChecks1": time.Now().UTC().UnixMilli(), + }) + + gasLimit, ok := api.checkSubmissionFeeRecipient(w, log, payload) + if !ok { + return + } + + log = log.WithField("timestampBeforeAttributesCheck", time.Now().UTC().UnixMilli()) + + ok = api.checkSubmissionPayloadAttrs(w, log, payload, eph.WithdrawalsRoot) + if !ok { + return + } + + // Verify the signature + log = log.WithField("timestampBeforeSignatureCheck", time.Now().UTC().UnixMilli()) + ok, err = boostTypes.VerifySignature(bid, api.opts.EthNetDetails.DomainBuilder, bid.BuilderPubkey[:], sig[:]) + log = log.WithField("timestampAfterSignatureCheck", time.Now().UTC().UnixMilli()) + if err != nil { + log.WithError(err).Warn("failed verifying builder signature") + api.RespondError(w, http.StatusBadRequest, "failed verifying builder signature") + return + } else if !ok { + log.Warn("invalid builder signature") + api.RespondError(w, http.StatusBadRequest, "invalid signature") + return + } + + // Create the redis pipeline tx + tx := api.redis.NewTxPipeline() + + // channel to send simulation result to the deferred function + simResultC := make(chan *blockSimResult, 1) + // var eligibleAt time.Time // will be set once the bid is ready + + bfOpts := bidFloorOpts{ + w: w, + tx: tx, + log: log, + cancellationsEnabled: isCancellationEnabled, + simResultC: simResultC, + payload: payload, + } + _, ok = api.checkFloorBidValue(bfOpts) + if !ok { + return + } + + nextTime = time.Now().UTC() + pf.Prechecks = uint64(nextTime.Sub(prevTime).Microseconds()) + + // Prepare the response data + getHeaderResponse, err := common.BuildGetHeaderResponseHeaderOnly(header.Message.Value, eph, api.blsSk, api.publicKey, api.opts.EthNetDetails.DomainBuilder) + if err != nil { + log.WithError(err).Error("could not sign builder bid") + api.RespondError(w, http.StatusBadRequest, err.Error()) + return + } + + bidTrace := &common.BidTraceV2{ //nolint:exhaustruct + BidTrace: *header.Message, + BlockNumber: header.ExecutionPayloadHeader.BlockNumber, + } + + // Save to Redis + updateBidResult, err := api.redis.SaveBidAndUpdateTopBid(context.Background(), tx, bidTrace, payload, nil, getHeaderResponse, receivedAt, isCancellationEnabled, nil) + if err != nil { + log.WithError(err).Error("could not save bid and update top bids") + api.RespondError(w, http.StatusInternalServerError, "failed saving and updating bid") + return + } + + // Add fields to logs + log = log.WithFields(logrus.Fields{ + "timestampAfterBidUpdate": time.Now().UTC().UnixMilli(), + "wasBidSavedInRedis": updateBidResult.WasBidSaved, + "wasTopBidUpdated": updateBidResult.WasTopBidUpdated, + "topBidValue": updateBidResult.TopBidValue, + "prevTopBidValue": updateBidResult.PrevTopBidValue, + "profileRedisSavePayloadUs": updateBidResult.TimeSavePayload.Microseconds(), + "profileRedisUpdateTopBidUs": updateBidResult.TimeUpdateTopBid.Microseconds(), + "profileRedisUpdateFloorUs": updateBidResult.TimeUpdateFloor.Microseconds(), + }) + + eligibleAt := time.Now().UTC() + + // Read all remaining bytes into the tee reader + remainder, err := io.ReadAll(r) + if err != nil { + demotionErr := fmt.Errorf("%w: could not read full message", err) + api.demoteBuilder(payload.BuilderPubkey().String(), payload, demotionErr) + log.WithError(err).Warn("could not read full message") + return + } + remainderReader := bytes.NewReader(remainder) + + slowPathOpts := v2SlowPathOpts{ + header: &header, + payload: payload, + receivedAt: receivedAt, + eligibleAt: eligibleAt, + pf: pf, + isCancellationEnabled: isCancellationEnabled, + entry: builderEntry, + gasLimit: gasLimit, + pipeliner: tx, + } + + // Join the header bytes with the remaining bytes. + go api.optimisticV2SlowPath(io.MultiReader(&buf, remainderReader), slowPathOpts) + + log.WithFields(logrus.Fields{ + "value": bid.Value.String(), + "profile": pf.String(), + }).Info("saving v2 optimistic bid from builder") + w.WriteHeader(http.StatusOK) +} + +type v2SlowPathOpts struct { + header *common.SubmitBlockRequestV2Optimistic + payload *common.BuilderSubmitBlockRequest + receivedAt time.Time + eligibleAt time.Time + pf common.Profile + isCancellationEnabled bool + entry *blockBuilderCacheEntry + gasLimit uint64 + pipeliner redis.Pipeliner +} + +func (api *RelayAPI) optimisticV2SlowPath(r io.Reader, v2Opts v2SlowPathOpts) { + log := api.log.WithFields(logrus.Fields{"method": "optimisticV2SlowPath"}) + + // TODO(mikeneuder): slow path + + // All done + log.Info("received v2 block from builder") +} + // --------------- // // INTERNAL APIS diff --git a/services/api/service_test.go b/services/api/service_test.go index 240f08f3..f41cc129 100644 --- a/services/api/service_test.go +++ b/services/api/service_test.go @@ -571,7 +571,8 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) backend.relay.proposerDutiesLock.RLock() backend.relay.proposerDutiesMap[tc.payload.Slot()] = tc.slotDuty backend.relay.proposerDutiesLock.RUnlock() @@ -638,33 +639,19 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { Message: &v1.BidTrace{ Slot: testSlot + 1, // submission for a future slot }, - }, - }, - expectOk: false, - }, - { - description: "failure_wrong_prev_randao", - attrs: payloadAttributesHelper{ - slot: testSlot, - payloadAttributes: beaconclient.PayloadAttributes{ - PrevRandao: testPrevRandao, - }, - }, - payload: &common.BuilderSubmitBlockRequest{ - Capella: &builderCapella.SubmitBlockRequest{ - Message: &v1.BidTrace{ - Slot: testSlot, - ParentHash: phase0.Hash32(parentHash), - }, ExecutionPayload: &capella.ExecutionPayload{ - PrevRandao: [32]byte(parentHash), // use a different hash to cause an error + Withdrawals: []*capella.Withdrawal{ + { + Index: 989694, + }, + }, }, }, }, expectOk: false, }, { - description: "failure_nil_withdrawals", + description: "failure_wrong_prev_randao", attrs: payloadAttributesHelper{ slot: testSlot, payloadAttributes: beaconclient.PayloadAttributes{ @@ -678,8 +665,12 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { ParentHash: phase0.Hash32(parentHash), }, ExecutionPayload: &capella.ExecutionPayload{ - PrevRandao: [32]byte(prevRandao), - Withdrawals: nil, // set to nil to cause an error + PrevRandao: [32]byte(parentHash), // use a different hash to cause an error + Withdrawals: []*capella.Withdrawal{ + { + Index: 989694, + }, + }, }, }, }, @@ -716,7 +707,8 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) backend.relay.payloadAttributesLock.RLock() backend.relay.payloadAttributes[testParentHash] = tc.attrs backend.relay.payloadAttributesLock.RUnlock() @@ -724,7 +716,9 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { w := httptest.NewRecorder() logger := logrus.New() log := logrus.NewEntry(logger) - ok := backend.relay.checkSubmissionPayloadAttrs(w, log, tc.payload) + withdrawalsRoot, err := ComputeWithdrawalsRoot(tc.payload.Withdrawals()) + require.Nil(t, err) + ok := backend.relay.checkSubmissionPayloadAttrs(w, log, tc.payload, withdrawalsRoot) require.Equal(t, tc.expectOk, ok) }) } @@ -785,7 +779,8 @@ func TestCheckSubmissionSlotDetails(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) headSlot := testSlot - 1 w := httptest.NewRecorder() @@ -848,7 +843,8 @@ func TestCheckBuilderEntry(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) backend.relay.blockBuildersCache[tc.pk.String()] = tc.entry backend.relay.ffDisableLowPrioBuilders = true w := httptest.NewRecorder() @@ -920,7 +916,8 @@ func TestCheckFloorBidValue(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) err := backend.redis.SetFloorBidValue(tc.payload.Slot(), tc.payload.ParentHash(), tc.payload.ProposerPubkey(), tc.floorValue) require.Nil(t, err) @@ -937,7 +934,7 @@ func TestCheckFloorBidValue(t *testing.T) { simResultC: simResultC, payload: tc.payload, } - floor, log, ok := backend.relay.checkFloorBidValue(bfOpts) + floor, ok := backend.relay.checkFloorBidValue(bfOpts) require.Equal(t, tc.expectOk, ok) if ok { require.NotNil(t, floor) @@ -994,7 +991,8 @@ func TestUpdateRedis(t *testing.T) { } for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - _, _, backend := startTestBackend(t) + pubkey, _ := generateKeyPair(t) + backend := startTestBackend(t, pubkey) w := httptest.NewRecorder() logger := logrus.New() log := logrus.NewEntry(logger)