From d019ad8eaf2d9eec6c85cf1f870205cfef08db7d Mon Sep 17 00:00:00 2001 From: shana Date: Wed, 26 Jul 2023 00:45:28 -0700 Subject: [PATCH] Remove submit block request wrapper (#485) * remove submit block request wrapper types * fix tests * fix lint --- .golangci.yaml | 1 + common/test_utils.go | 7 +- common/types.go | 187 +++------------------------ common/types_spec.go | 20 ++- common/utils.go | 111 +++++++++++++++- database/database.go | 68 +++++----- database/database_test.go | 4 +- database/mockdb.go | 16 ++- database/typesconv.go | 15 ++- datastore/memcached_test.go | 55 +++++--- datastore/redis.go | 31 +++-- go.mod | 2 + go.sum | 4 +- services/api/blocksim_ratelimiter.go | 6 +- services/api/optimistic_test.go | 6 +- services/api/service.go | 126 +++++++++--------- services/api/service_test.go | 68 ++++++---- services/api/utils.go | 11 +- 18 files changed, 394 insertions(+), 344 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 7ef8310d..baa7e72a 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -71,6 +71,7 @@ linters-settings: gomoddirectives: replace-allow-list: - github.com/attestantio/go-eth2-client + - github.com/attestantio/go-builder-client maintidx: under: 5 diff --git a/common/test_utils.go b/common/test_utils.go index 26f991f1..29aae959 100644 --- a/common/test_utils.go +++ b/common/test_utils.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/attestantio/go-builder-client/api/capella" + "github.com/attestantio/go-builder-client/spec" + consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/bellatrix" consensuscapella "github.com/attestantio/go-eth2-client/spec/capella" "github.com/flashbots/go-boost-utils/bls" @@ -64,10 +66,11 @@ var ValidPayloadRegisterValidator = boostTypes.SignedValidatorRegistration{ "0xaf12df007a0c78abb5575067e5f8b089cfcc6227e4a91db7dd8cf517fe86fb944ead859f0781277d9b78c672e4a18c5d06368b603374673cf2007966cece9540f3a1b3f6f9e1bf421d779c4e8010368e6aac134649c7a009210780d401a778a5"), } -func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2) BuilderSubmitBlockRequest { +func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2) spec.VersionedSubmitBlockRequest { signature, err := boostTypes.SignMessage(bid, boostTypes.DomainBuilder, sk) check(err, " SignMessage: ", bid, sk) - return BuilderSubmitBlockRequest{ + return spec.VersionedSubmitBlockRequest{ //nolint:exhaustruct + Version: consensusspec.DataVersionCapella, Capella: &capella.SubmitBlockRequest{ Message: &bid.BidTrace, Signature: [96]byte(signature), diff --git a/common/types.go b/common/types.go index a58cff98..123ee08c 100644 --- a/common/types.go +++ b/common/types.go @@ -4,18 +4,15 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "os" - "github.com/attestantio/go-builder-client/api" - "github.com/attestantio/go-builder-client/api/capella" apiv1 "github.com/attestantio/go-builder-client/api/v1" - consensusspec "github.com/attestantio/go-eth2-client/spec" consensusbellatrix "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" ssz "github.com/ferranbt/fastssz" boostTypes "github.com/flashbots/go-boost-utils/types" + "github.com/holiman/uint256" ) var ( @@ -294,169 +291,25 @@ func (b *BidTraceV2WithTimestampJSON) ToCSVRecord() []string { } } -type BuilderSubmitBlockRequest struct { - Capella *capella.SubmitBlockRequest -} - -func (b *BuilderSubmitBlockRequest) MarshalJSON() ([]byte, error) { - if b.Capella != nil { - return json.Marshal(b.Capella) - } - return nil, ErrEmptyPayload -} - -func (b *BuilderSubmitBlockRequest) UnmarshalJSON(data []byte) error { - capella := new(capella.SubmitBlockRequest) - err := json.Unmarshal(data, capella) - if err != nil { - return err - } - b.Capella = capella - return nil -} - -func (b *BuilderSubmitBlockRequest) HasExecutionPayload() bool { - if b.Capella != nil { - return b.Capella.ExecutionPayload != nil - } - return false -} - -func (b *BuilderSubmitBlockRequest) ExecutionPayloadResponse() (*api.VersionedExecutionPayload, error) { - if b.Capella != nil { - return &api.VersionedExecutionPayload{ - Version: consensusspec.DataVersionCapella, - Capella: b.Capella.ExecutionPayload, - }, nil - } - - return nil, ErrEmptyPayload -} - -func (b *BuilderSubmitBlockRequest) Slot() uint64 { - if b.Capella != nil { - return b.Capella.Message.Slot - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) BlockHash() string { - if b.Capella != nil { - return b.Capella.Message.BlockHash.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) ExecutionPayloadBlockHash() string { - if b.Capella != nil { - return b.Capella.ExecutionPayload.BlockHash.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) BuilderPubkey() phase0.BLSPubKey { - if b.Capella != nil { - return b.Capella.Message.BuilderPubkey - } - return phase0.BLSPubKey{} -} - -func (b *BuilderSubmitBlockRequest) ProposerFeeRecipient() string { - if b.Capella != nil { - return b.Capella.Message.ProposerFeeRecipient.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) Timestamp() uint64 { - if b.Capella != nil { - return b.Capella.ExecutionPayload.Timestamp - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) ProposerPubkey() string { - if b.Capella != nil { - return b.Capella.Message.ProposerPubkey.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) ParentHash() string { - if b.Capella != nil { - return b.Capella.Message.ParentHash.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) ExecutionPayloadParentHash() string { - if b.Capella != nil { - return b.Capella.ExecutionPayload.ParentHash.String() - } - return "" -} - -func (b *BuilderSubmitBlockRequest) Value() *big.Int { - if b.Capella != nil { - return b.Capella.Message.Value.ToBig() - } - return nil -} - -func (b *BuilderSubmitBlockRequest) NumTx() int { - if b.Capella != nil { - return len(b.Capella.ExecutionPayload.Transactions) - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) BlockNumber() uint64 { - if b.Capella != nil { - return b.Capella.ExecutionPayload.BlockNumber - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) GasUsed() uint64 { - if b.Capella != nil { - return b.Capella.ExecutionPayload.GasUsed - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) GasLimit() uint64 { - if b.Capella != nil { - return b.Capella.ExecutionPayload.GasLimit - } - return 0 -} - -func (b *BuilderSubmitBlockRequest) Signature() phase0.BLSSignature { - if b.Capella != nil { - return b.Capella.Signature - } - return phase0.BLSSignature{} -} - -func (b *BuilderSubmitBlockRequest) Random() string { - if b.Capella != nil { - return fmt.Sprintf("%#x", b.Capella.ExecutionPayload.PrevRandao) - } - return "" -} - -func (b *BuilderSubmitBlockRequest) Message() *apiv1.BidTrace { - if b.Capella != nil { - return b.Capella.Message - } - return nil -} - -func (b *BuilderSubmitBlockRequest) Withdrawals() []*consensuscapella.Withdrawal { - if b.Capella != nil { - return b.Capella.ExecutionPayload.Withdrawals - } - return nil +type BlockSubmissionInfo struct { + BidTrace *apiv1.BidTrace + Slot uint64 + BlockHash phase0.Hash32 + ParentHash phase0.Hash32 + ExecutionPayloadBlockHash phase0.Hash32 + ExecutionPayloadParentHash phase0.Hash32 + Builder phase0.BLSPubKey + Proposer phase0.BLSPubKey + ProposerFeeRecipient consensusbellatrix.ExecutionAddress + GasUsed uint64 + GasLimit uint64 + Timestamp uint64 + BlockNumber uint64 + Value *uint256.Int + PrevRandao phase0.Hash32 + Signature phase0.BLSSignature + Transactions []consensusbellatrix.Transaction + Withdrawals []*consensuscapella.Withdrawal } /* diff --git a/common/types_spec.go b/common/types_spec.go index e37cb955..26f02d1e 100644 --- a/common/types_spec.go +++ b/common/types_spec.go @@ -2,7 +2,7 @@ package common import ( "encoding/json" - "errors" + "fmt" "github.com/attestantio/go-builder-client/api" "github.com/attestantio/go-builder-client/api/capella" @@ -15,12 +15,14 @@ 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/pkg/errors" ) var ( ErrMissingRequest = errors.New("req is nil") ErrMissingSecretKey = errors.New("secret key is nil") ErrInvalidTransaction = errors.New("invalid transaction") + ErrInvalidVersion = errors.New("invalid version") ) type HTTPErrorResp struct { @@ -32,7 +34,7 @@ var NilResponse = struct{}{} var ZeroU256 = boostTypes.IntToU256(0) -func BuildGetHeaderResponse(payload *BuilderSubmitBlockRequest, sk *bls.SecretKey, pubkey *boostTypes.PublicKey, domain boostTypes.Domain) (*spec.VersionedSignedBuilderBid, error) { +func BuildGetHeaderResponse(payload *spec.VersionedSubmitBlockRequest, sk *bls.SecretKey, pubkey *boostTypes.PublicKey, domain boostTypes.Domain) (*spec.VersionedSignedBuilderBid, error) { if payload == nil { return nil, ErrMissingRequest } @@ -55,7 +57,7 @@ func BuildGetHeaderResponse(payload *BuilderSubmitBlockRequest, sk *bls.SecretKe return nil, ErrEmptyPayload } -func BuildGetPayloadResponse(payload *BuilderSubmitBlockRequest) (*api.VersionedExecutionPayload, error) { +func BuildGetPayloadResponse(payload *spec.VersionedSubmitBlockRequest) (*api.VersionedExecutionPayload, error) { if payload.Capella != nil { return &api.VersionedExecutionPayload{ Version: consensusspec.DataVersionCapella, @@ -179,12 +181,20 @@ func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *consensusap } type BuilderBlockValidationRequest struct { - BuilderSubmitBlockRequest + spec.VersionedSubmitBlockRequest RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` } func (r *BuilderBlockValidationRequest) MarshalJSON() ([]byte, error) { - blockRequest, err := r.BuilderSubmitBlockRequest.MarshalJSON() + var blockRequest []byte + var err error + + switch r.VersionedSubmitBlockRequest.Version { //nolint:exhaustive + case consensusspec.DataVersionCapella: + blockRequest, err = r.VersionedSubmitBlockRequest.Capella.MarshalJSON() + default: + return nil, errors.Wrap(ErrInvalidVersion, fmt.Sprintf("%d is not supported", r.VersionedSubmitBlockRequest.Version)) + } if err != nil { return nil, err } diff --git a/common/utils.go b/common/utils.go index 77ad2ecb..76ee403e 100644 --- a/common/utils.go +++ b/common/utils.go @@ -19,6 +19,7 @@ import ( "github.com/attestantio/go-builder-client/api/capella" v1 "github.com/attestantio/go-builder-client/api/v1" "github.com/attestantio/go-builder-client/spec" + consensusspec "github.com/attestantio/go-eth2-client/spec" capellaspec "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" ethcommon "github.com/ethereum/go-ethereum/common" @@ -183,7 +184,7 @@ type CreateTestBlockSubmissionOpts struct { ProposerPubkey string } -func CreateTestBlockSubmission(t *testing.T, builderPubkey string, value *uint256.Int, opts *CreateTestBlockSubmissionOpts) (payload *BuilderSubmitBlockRequest, getPayloadResponse *api.VersionedExecutionPayload, getHeaderResponse *spec.VersionedSignedBuilderBid) { +func CreateTestBlockSubmission(t *testing.T, builderPubkey string, value *uint256.Int, opts *CreateTestBlockSubmissionOpts) (payload *spec.VersionedSubmitBlockRequest, getPayloadResponse *api.VersionedExecutionPayload, getHeaderResponse *spec.VersionedSignedBuilderBid) { t.Helper() var err error @@ -214,7 +215,8 @@ func CreateTestBlockSubmission(t *testing.T, builderPubkey string, value *uint25 builderPk, err := StrToPhase0Pubkey(builderPubkey) require.NoError(t, err) - payload = &BuilderSubmitBlockRequest{ + payload = &spec.VersionedSubmitBlockRequest{ //nolint:exhaustruct + Version: consensusspec.DataVersionCapella, Capella: &capella.SubmitBlockRequest{ Message: &v1.BidTrace{ //nolint:exhaustruct BuilderPubkey: builderPk, @@ -248,3 +250,108 @@ func GetEnvDurationSec(key string, defaultValueSec int) time.Duration { } return time.Duration(defaultValueSec) * time.Second } + +func GetBlockSubmissionInfo(submission *spec.VersionedSubmitBlockRequest) (*BlockSubmissionInfo, error) { + bidTrace, err := submission.BidTrace() + if err != nil { + return nil, err + } + signature, err := submission.Signature() + if err != nil { + return nil, err + } + slot, err := submission.Slot() + if err != nil { + return nil, err + } + blockHash, err := submission.BlockHash() + if err != nil { + return nil, err + } + parentHash, err := submission.ParentHash() + if err != nil { + return nil, err + } + executionPayloadBlockHash, err := submission.ExecutionPayloadBlockHash() + if err != nil { + return nil, err + } + executionPayloadParentHash, err := submission.ExecutionPayloadParentHash() + if err != nil { + return nil, err + } + builder, err := submission.Builder() + if err != nil { + return nil, err + } + proposerPubkey, err := submission.ProposerPubKey() + if err != nil { + return nil, err + } + proposerFeeRecipient, err := submission.ProposerFeeRecipient() + if err != nil { + return nil, err + } + gasUsed, err := submission.GasUsed() + if err != nil { + return nil, err + } + gasLimit, err := submission.GasLimit() + if err != nil { + return nil, err + } + timestamp, err := submission.Timestamp() + if err != nil { + return nil, err + } + txs, err := submission.Transactions() + if err != nil { + return nil, err + } + value, err := submission.Value() + if err != nil { + return nil, err + } + blockNumber, err := submission.BlockNumber() + if err != nil { + return nil, err + } + prevRandao, err := submission.PrevRandao() + if err != nil { + return nil, err + } + withdrawals, err := submission.Withdrawals() + if err != nil { + return nil, err + } + return &BlockSubmissionInfo{ + BidTrace: bidTrace, + Signature: signature, + Slot: slot, + BlockHash: blockHash, + ParentHash: parentHash, + ExecutionPayloadBlockHash: executionPayloadBlockHash, + ExecutionPayloadParentHash: executionPayloadParentHash, + Builder: builder, + Proposer: proposerPubkey, + ProposerFeeRecipient: proposerFeeRecipient, + GasUsed: gasUsed, + GasLimit: gasLimit, + Timestamp: timestamp, + Transactions: txs, + Value: value, + PrevRandao: prevRandao, + BlockNumber: blockNumber, + Withdrawals: withdrawals, + }, nil +} + +func GetBlockSubmissionExecutionPayload(submission *spec.VersionedSubmitBlockRequest) (*api.VersionedExecutionPayload, error) { + if submission.Capella != nil { + return &api.VersionedExecutionPayload{ + Version: consensusspec.DataVersionCapella, + Capella: submission.Capella.ExecutionPayload, + }, nil + } + return nil, ErrEmptyPayload +} diff --git a/database/database.go b/database/database.go index afdac7b6..555da5f7 100644 --- a/database/database.go +++ b/database/database.go @@ -9,8 +9,9 @@ import ( "strings" "time" + "github.com/attestantio/go-builder-client/spec" consensusapi "github.com/attestantio/go-eth2-client/api" - "github.com/attestantio/go-eth2-client/spec" + consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/flashbots/go-boost-utils/types" "github.com/flashbots/mev-boost-relay/common" "github.com/flashbots/mev-boost-relay/database/migrations" @@ -27,7 +28,7 @@ type IDatabaseService interface { GetValidatorRegistration(pubkey string) (*ValidatorRegistrationEntry, error) GetValidatorRegistrationsForPubkeys(pubkeys []string) ([]*ValidatorRegistrationEntry, error) - SaveBuilderBlockSubmission(payload *common.BuilderSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) + SaveBuilderBlockSubmission(payload *spec.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) GetBlockSubmissionEntry(slot uint64, proposerPubkey, blockHash string) (entry *BuilderBlockSubmissionEntry, err error) GetBuilderSubmissions(filters GetBuilderSubmissionsFilters) ([]*BuilderBlockSubmissionEntry, error) GetBuilderSubmissionsBySlots(slotFrom, slotTo uint64) (entries []*BuilderBlockSubmissionEntry, err error) @@ -49,8 +50,8 @@ type IDatabaseService interface { UpsertBlockBuilderEntryAfterSubmission(lastSubmission *BuilderBlockSubmissionEntry, isError bool) error IncBlockBuilderStatsAfterGetPayload(builderPubkey string) error - InsertBuilderDemotion(submitBlockRequest *common.BuilderSubmitBlockRequest, simError error) error - UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *spec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error + InsertBuilderDemotion(submitBlockRequest *spec.VersionedSubmitBlockRequest, simError error) error + UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *consensusspec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error GetBuilderDemotion(trace *common.BidTraceV2) (*BuilderDemotionEntry, error) GetTooLateGetPayload(slot uint64) (entries []*TooLateGetPayloadEntry, err error) @@ -177,7 +178,7 @@ func (s *DatabaseService) GetLatestValidatorRegistrations(timestampOnly bool) ([ return registrations, err } -func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.BuilderSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) { +func (s *DatabaseService) SaveBuilderBlockSubmission(payload *spec.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) { // Save execution_payload: insert, or if already exists update to be able to return the id ('on conflict do nothing' doesn't return an id) execPayloadEntry, err := PayloadToExecPayloadEntry(payload) if err != nil { @@ -202,6 +203,11 @@ func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.BuilderSubm requestErrStr = requestError.Error() } + submission, err := common.GetBlockSubmissionInfo(payload) + if err != nil { + return nil, err + } + blockSubmissionEntry := &BuilderBlockSubmissionEntry{ ReceivedAt: NewNullTime(receivedAt), EligibleAt: NewNullTime(eligibleAt), @@ -212,24 +218,24 @@ func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.BuilderSubm SimError: simErrStr, SimReqError: requestErrStr, - Signature: payload.Signature().String(), + Signature: submission.Signature.String(), - Slot: payload.Slot(), - BlockHash: payload.BlockHash(), - ParentHash: payload.ParentHash(), + Slot: submission.Slot, + BlockHash: submission.BlockHash.String(), + ParentHash: submission.ParentHash.String(), - BuilderPubkey: payload.BuilderPubkey().String(), - ProposerPubkey: payload.ProposerPubkey(), - ProposerFeeRecipient: payload.ProposerFeeRecipient(), + BuilderPubkey: submission.Builder.String(), + ProposerPubkey: submission.Proposer.String(), + ProposerFeeRecipient: submission.ProposerFeeRecipient.String(), - GasUsed: payload.GasUsed(), - GasLimit: payload.GasLimit(), + GasUsed: submission.GasUsed, + GasLimit: submission.GasLimit, - NumTx: uint64(payload.NumTx()), - Value: payload.Value().String(), + NumTx: uint64(len(submission.Transactions)), + Value: submission.Value.Dec(), - Epoch: payload.Slot() / common.SlotsPerEpoch, - BlockNumber: payload.BlockNumber(), + Epoch: submission.Slot / common.SlotsPerEpoch, + BlockNumber: submission.BlockNumber, DecodeDuration: profile.Decode, PrechecksDuration: profile.Prechecks, @@ -538,24 +544,28 @@ func (s *DatabaseService) DeleteExecutionPayloads(idFirst, idLast uint64) error return err } -func (s *DatabaseService) InsertBuilderDemotion(submitBlockRequest *common.BuilderSubmitBlockRequest, simError error) error { - _submitBlockRequest, err := json.Marshal(submitBlockRequest) +func (s *DatabaseService) InsertBuilderDemotion(submitBlockRequest *spec.VersionedSubmitBlockRequest, simError error) error { + _submitBlockRequest, err := json.Marshal(submitBlockRequest.Capella) + if err != nil { + return err + } + submission, err := common.GetBlockSubmissionInfo(submitBlockRequest) if err != nil { return err } builderDemotionEntry := BuilderDemotionEntry{ SubmitBlockRequest: NewNullString(string(_submitBlockRequest)), - Epoch: submitBlockRequest.Slot() / common.SlotsPerEpoch, - Slot: submitBlockRequest.Slot(), + Epoch: submission.Slot / common.SlotsPerEpoch, + Slot: submission.Slot, - BuilderPubkey: submitBlockRequest.BuilderPubkey().String(), - ProposerPubkey: submitBlockRequest.ProposerPubkey(), + BuilderPubkey: submission.Builder.String(), + ProposerPubkey: submission.Proposer.String(), - Value: submitBlockRequest.Value().String(), - FeeRecipient: submitBlockRequest.ProposerFeeRecipient(), + Value: submission.Value.Dec(), + FeeRecipient: submission.ProposerFeeRecipient.String(), - BlockHash: submitBlockRequest.BlockHash(), + BlockHash: submission.BlockHash.String(), SimError: simError.Error(), } @@ -567,8 +577,8 @@ func (s *DatabaseService) InsertBuilderDemotion(submitBlockRequest *common.Build return err } -func (s *DatabaseService) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *spec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error { - _signedBeaconBlock, err := json.Marshal(signedBlock) +func (s *DatabaseService) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *consensusspec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error { + _signedBeaconBlock, err := json.Marshal(signedBlock.Capella) if err != nil { return err } diff --git a/database/database_test.go b/database/database_test.go index 3381d4e5..2648b325 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -90,7 +90,9 @@ func insertTestBuilder(t *testing.T, db IDatabaseService) string { require.NoError(t, err) err = db.UpsertBlockBuilderEntryAfterSubmission(entry, false) require.NoError(t, err) - return req.BuilderPubkey().String() + builderPubkey, err := req.Builder() + require.NoError(t, err) + return builderPubkey.String() } func resetDatabase(t *testing.T) *DatabaseService { diff --git a/database/mockdb.go b/database/mockdb.go index 5679b242..f2c9c28b 100644 --- a/database/mockdb.go +++ b/database/mockdb.go @@ -5,8 +5,9 @@ import ( "fmt" "time" + "github.com/attestantio/go-builder-client/spec" consensusapi "github.com/attestantio/go-eth2-client/api" - "github.com/attestantio/go-eth2-client/spec" + consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/flashbots/go-boost-utils/types" "github.com/flashbots/mev-boost-relay/common" ) @@ -38,7 +39,7 @@ func (db MockDB) GetLatestValidatorRegistrations(timestampOnly bool) ([]*Validat return nil, nil } -func (db MockDB) SaveBuilderBlockSubmission(payload *common.BuilderSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) { +func (db MockDB) SaveBuilderBlockSubmission(payload *spec.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) { return nil, nil } @@ -155,13 +156,16 @@ func (db MockDB) IncBlockBuilderStatsAfterGetPayload(builderPubkey string) error return nil } -func (db MockDB) InsertBuilderDemotion(submitBlockRequest *common.BuilderSubmitBlockRequest, simError error) error { - pubkey := submitBlockRequest.BuilderPubkey().String() - db.Demotions[pubkey] = true +func (db MockDB) InsertBuilderDemotion(submitBlockRequest *spec.VersionedSubmitBlockRequest, simError error) error { + pubkey, err := submitBlockRequest.Builder() + if err != nil { + return err + } + db.Demotions[pubkey.String()] = true return nil } -func (db MockDB) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *spec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error { +func (db MockDB) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *consensusspec.VersionedSignedBeaconBlock, signedRegistration *types.SignedValidatorRegistration) error { pubkey := trace.BuilderPubkey.String() _, ok := db.Builders[pubkey] if !ok { diff --git a/database/typesconv.go b/database/typesconv.go index 1df7abc2..08e205e4 100644 --- a/database/typesconv.go +++ b/database/typesconv.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/attestantio/go-builder-client/api" + "github.com/attestantio/go-builder-client/spec" consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/flashbots/mev-boost-relay/common" @@ -12,7 +13,7 @@ import ( var ErrUnsupportedExecutionPayload = errors.New("unsupported execution payload version") -func PayloadToExecPayloadEntry(payload *common.BuilderSubmitBlockRequest) (*ExecutionPayloadEntry, error) { +func PayloadToExecPayloadEntry(payload *spec.VersionedSubmitBlockRequest) (*ExecutionPayloadEntry, error) { var _payload []byte var version string var err error @@ -23,10 +24,16 @@ func PayloadToExecPayloadEntry(payload *common.BuilderSubmitBlockRequest) (*Exec } version = common.ForkVersionStringCapella } + + submission, err := common.GetBlockSubmissionInfo(payload) + if err != nil { + return nil, err + } + return &ExecutionPayloadEntry{ - Slot: payload.Slot(), - ProposerPubkey: payload.ProposerPubkey(), - BlockHash: payload.BlockHash(), + Slot: submission.Slot, + ProposerPubkey: submission.Proposer.String(), + BlockHash: submission.BlockHash.String(), Version: version, Payload: string(_payload), diff --git a/datastore/memcached_test.go b/datastore/memcached_test.go index 4ec0fbda..5bdf8db8 100644 --- a/datastore/memcached_test.go +++ b/datastore/memcached_test.go @@ -11,6 +11,7 @@ import ( "github.com/attestantio/go-builder-client/api" "github.com/attestantio/go-builder-client/api/capella" apiv1 "github.com/attestantio/go-builder-client/api/v1" + "github.com/attestantio/go-builder-client/spec" consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/bellatrix" capellaspec "github.com/attestantio/go-eth2-client/spec/capella" @@ -31,10 +32,10 @@ var ( ErrNoMemcachedServers = errors.New("no memcached servers specified") ) -func testBuilderSubmitBlockRequest(pubkey phase0.BLSPubKey, signature phase0.BLSSignature, version consensusspec.DataVersion) common.BuilderSubmitBlockRequest { +func testBuilderSubmitBlockRequest(pubkey phase0.BLSPubKey, signature phase0.BLSSignature, version consensusspec.DataVersion) spec.VersionedSubmitBlockRequest { switch version { case consensusspec.DataVersionCapella: - return common.BuilderSubmitBlockRequest{ + return spec.VersionedSubmitBlockRequest{ Capella: &capella.SubmitBlockRequest{ Signature: signature, Message: &apiv1.BidTrace{ @@ -71,7 +72,7 @@ func testBuilderSubmitBlockRequest(pubkey phase0.BLSPubKey, signature phase0.BLS case consensusspec.DataVersionUnknown, consensusspec.DataVersionPhase0, consensusspec.DataVersionAltair, consensusspec.DataVersionBellatrix: fallthrough default: - return common.BuilderSubmitBlockRequest{ + return spec.VersionedSubmitBlockRequest{ Capella: nil, } } @@ -108,7 +109,7 @@ func initMemcached(t *testing.T) (mem *Memcached, err error) { // RUN_INTEGRATION_TESTS=1 MEMCACHED_URIS="localhost:11211" go test -v -run ".*Memcached.*" ./... func TestMemcached(t *testing.T) { type test struct { - Input common.BuilderSubmitBlockRequest + Input spec.VersionedSubmitBlockRequest Description string TestSuite func(tc *test) func(*testing.T) } @@ -135,7 +136,7 @@ func TestMemcached(t *testing.T) { TestSuite: func(tc *test) func(*testing.T) { return func(t *testing.T) { t.Helper() - payload, err := tc.Input.ExecutionPayloadResponse() + payload, err := common.GetBlockSubmissionExecutionPayload(&tc.Input) require.Error(t, err) require.Equal(t, err, common.ErrEmptyPayload) require.Nil(t, payload) @@ -149,7 +150,7 @@ func TestMemcached(t *testing.T) { return func(t *testing.T) { t.Helper() - payload, err := tc.Input.ExecutionPayloadResponse() + payload, err := common.GetBlockSubmissionExecutionPayload(&tc.Input) require.NoError( t, err, @@ -175,15 +176,20 @@ func TestMemcached(t *testing.T) { require.NoError(t, err) require.True(t, bytes.Equal(inputBytes, outputBytes)) + submission, err := common.GetBlockSubmissionInfo(&tc.Input) + require.NoError(t, err) + // key should not exist in cache yet - empty, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + empty, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err) require.Nil(t, empty) - err = mem.SaveExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash(), payload) + submission, err = common.GetBlockSubmissionInfo(&tc.Input) + require.NoError(t, err) + err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) require.NoError(t, err) - get, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + get, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err, "expected no error when fetching execution payload from memcached but found [%v]", err) getBytes, err := get.MarshalJSON() @@ -200,27 +206,30 @@ func TestMemcached(t *testing.T) { return func(t *testing.T) { t.Helper() - payload, err := tc.Input.ExecutionPayloadResponse() + payload, err := common.GetBlockSubmissionExecutionPayload(&tc.Input) require.NoError( t, err, "expected valid execution payload response for builder's submit block request but found [%v]", err, ) - err = mem.SaveExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash(), payload) + submission, err := common.GetBlockSubmissionInfo(&tc.Input) + require.NoError(t, err) + + err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) require.NoError(t, err) - prev, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + prev, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err) - require.Equal(t, len(prev.Capella.Transactions), tc.Input.NumTx()) + require.Equal(t, len(prev.Capella.Transactions), len(submission.Transactions)) payload.Bellatrix.GasLimit++ require.NotEqual(t, prev.Bellatrix.GasLimit, payload.Bellatrix.GasLimit) - err = mem.SaveExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash(), payload) + err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) require.NoError(t, err) - current, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + current, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err) require.Equal(t, current.Bellatrix.GasLimit, payload.Bellatrix.GasLimit) require.NotEqual(t, current.Bellatrix.GasLimit, prev.Bellatrix.GasLimit) @@ -242,23 +251,27 @@ func TestMemcached(t *testing.T) { require.NoError(t, err) tc.Input.Capella.Message.ProposerPubkey = phase0.BLSPubKey(pk) - payload, err := tc.Input.ExecutionPayloadResponse() + payload, err := common.GetBlockSubmissionExecutionPayload(&tc.Input) require.NoError( t, err, "expected valid execution payload response for builder's submit block request but found [%v]", err, ) - require.Equal(t, tc.Input.ProposerPubkey(), pk.String()) - err = mem.SaveExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash(), payload) + submission, err := common.GetBlockSubmissionInfo(&tc.Input) + require.NoError(t, err) + + require.Equal(t, submission.Proposer.String(), pk.String()) + + err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) require.NoError(t, err) - ret, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + ret, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err) - require.Equal(t, len(ret.Capella.Transactions), tc.Input.NumTx()) + require.Equal(t, len(ret.Capella.Transactions), len(submission.Transactions)) time.Sleep((time.Duration(defaultMemcachedExpirySeconds) + 2) * time.Second) - expired, err := mem.GetExecutionPayload(tc.Input.Slot(), tc.Input.ProposerPubkey(), tc.Input.BlockHash()) + expired, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) require.NoError(t, err) require.NotEqual(t, ret, expired) require.Nil(t, expired) diff --git a/datastore/redis.go b/datastore/redis.go index bc25dea7..f6a82b41 100644 --- a/datastore/redis.go +++ b/datastore/redis.go @@ -462,19 +462,24 @@ type SaveBidAndUpdateTopBidResponse struct { TimeUpdateFloor time.Duration } -func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis.Pipeliner, trace *common.BidTraceV2, payload *common.BuilderSubmitBlockRequest, getPayloadResponse *api.VersionedExecutionPayload, getHeaderResponse *spec.VersionedSignedBuilderBid, reqReceivedAt time.Time, isCancellationEnabled bool, floorValue *big.Int) (state SaveBidAndUpdateTopBidResponse, err error) { +func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis.Pipeliner, trace *common.BidTraceV2, payload *spec.VersionedSubmitBlockRequest, getPayloadResponse *api.VersionedExecutionPayload, getHeaderResponse *spec.VersionedSignedBuilderBid, reqReceivedAt time.Time, isCancellationEnabled bool, floorValue *big.Int) (state SaveBidAndUpdateTopBidResponse, err error) { var prevTime, nextTime time.Time prevTime = time.Now() + submission, err := common.GetBlockSubmissionInfo(payload) + if err != nil { + return state, err + } + // Load latest bids for a given slot+parent+proposer - builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, payload.Slot(), payload.ParentHash(), payload.ProposerPubkey()) + builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) if err != nil { return state, err } // Load floor value (if not passed in already) if floorValue == nil { - floorValue, err = r.GetFloorBidValue(ctx, pipeliner, payload.Slot(), payload.ParentHash(), payload.ProposerPubkey()) + floorValue, err = r.GetFloorBidValue(ctx, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) if err != nil { return state, err } @@ -488,7 +493,7 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis state.PrevTopBidValue = state.TopBidValue // Abort now if non-cancellation bid is lower than floor value - isBidAboveFloor := payload.Value().Cmp(floorValue) == 1 + isBidAboveFloor := submission.Value.ToBig().Cmp(floorValue) == 1 if !isCancellationEnabled && !isBidAboveFloor { return state, nil } @@ -502,7 +507,7 @@ 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) + err = r.SaveExecutionPayloadCapella(ctx, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), getPayloadResponse.Capella) if err != nil { return state, err } @@ -513,11 +518,11 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis prevTime = nextTime // 2. Save latest bid for this builder - err = r.SaveBuilderBid(ctx, pipeliner, payload.Slot(), payload.ParentHash(), payload.ProposerPubkey(), payload.BuilderPubkey().String(), reqReceivedAt, getHeaderResponse) + err = r.SaveBuilderBid(ctx, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), submission.Builder.String(), reqReceivedAt, getHeaderResponse) if err != nil { return state, err } - builderBids.bidValues[payload.BuilderPubkey().String()] = payload.Value() + builderBids.bidValues[submission.Builder.String()] = submission.Value.ToBig() // Record time needed to save bid nextTime = time.Now().UTC() @@ -541,11 +546,11 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis return state, nil } - state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, payload.Slot(), payload.ParentHash(), payload.ProposerPubkey(), floorValue) + state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), floorValue) if err != nil { return state, err } - state.IsNewTopBid = payload.Value().Cmp(state.TopBidValue) == 0 + state.IsNewTopBid = submission.Value.ToBig().Cmp(state.TopBidValue) == 0 // An Exec happens in _updateTopBid. state.WasBidSaved = true @@ -559,8 +564,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis } // Non-cancellable bid above floor should set new floor - keyBidSource := r.keyLatestBidByBuilder(payload.Slot(), payload.ParentHash(), payload.ProposerPubkey(), payload.BuilderPubkey().String()) - keyFloorBid := r.keyFloorBid(payload.Slot(), payload.ParentHash(), payload.ProposerPubkey()) + keyBidSource := r.keyLatestBidByBuilder(submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), submission.Builder.String()) + keyFloorBid := r.keyFloorBid(submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) c := pipeliner.Copy(ctx, keyBidSource, keyFloorBid, 0, true) _, err = pipeliner.Exec(ctx) if err != nil { @@ -578,8 +583,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis return state, err } - keyFloorBidValue := r.keyFloorBidValue(payload.Slot(), payload.ParentHash(), payload.ProposerPubkey()) - err = pipeliner.Set(ctx, keyFloorBidValue, payload.Value().String(), expiryBidCache).Err() + keyFloorBidValue := r.keyFloorBidValue(submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) + err = pipeliner.Set(ctx, keyFloorBidValue, submission.Value.Dec(), expiryBidCache).Err() if err != nil { return state, err } diff --git a/go.mod b/go.mod index 123dcb43..377b484a 100644 --- a/go.mod +++ b/go.mod @@ -123,3 +123,5 @@ retract ( ) replace github.com/attestantio/go-eth2-client => github.com/avalonche/go-eth2-client v0.0.0-20230720061256-82081347600e + +replace github.com/attestantio/go-builder-client => github.com/avalonche/go-builder-client v0.0.0-20230711071258-c1ca05d6e8b7 diff --git a/go.sum b/go.sum index 155937ec..541981b3 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/alicebob/miniredis/v2 v2.31.0/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CAS github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/attestantio/go-builder-client v0.3.2-0.20230701110827-d0ecfee1ab62 h1:jtNd8modhHUKfgLcQBv6ajgBNldwkIXT4U2iqWtbyA0= -github.com/attestantio/go-builder-client v0.3.2-0.20230701110827-d0ecfee1ab62/go.mod h1:DwesMTOqnCp4u+n3uZ+fWL8wwnSBZVD9VMIVPDR+AZE= +github.com/avalonche/go-builder-client v0.0.0-20230711071258-c1ca05d6e8b7 h1:NQt83tMbCmK2V1OuPUUEIudvi8EeULl3iWG3Ht1bdCk= +github.com/avalonche/go-builder-client v0.0.0-20230711071258-c1ca05d6e8b7/go.mod h1:DwesMTOqnCp4u+n3uZ+fWL8wwnSBZVD9VMIVPDR+AZE= github.com/avalonche/go-eth2-client v0.0.0-20230720061256-82081347600e h1:WhEhbE/udrh+M8MltkFhXy4BCTB+Q3ubinTdTidt10I= github.com/avalonche/go-eth2-client v0.0.0-20230720061256-82081347600e/go.mod h1:KSVlZSW1A3jUg5H8O89DLtqxgJprRfTtI7k89fLdhu0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= diff --git a/services/api/blocksim_ratelimiter.go b/services/api/blocksim_ratelimiter.go index 74fdbb8a..c5633185 100644 --- a/services/api/blocksim_ratelimiter.go +++ b/services/api/blocksim_ratelimiter.go @@ -74,10 +74,14 @@ func (b *BlockSimulationRateLimiter) Send(context context.Context, payload *comm return ErrNoCapellaPayload, nil } // TODO: add deneb support. + submission, err := common.GetBlockSubmissionInfo(&payload.VersionedSubmitBlockRequest) + if err != nil { + return err, nil + } // Prepare headers headers := http.Header{} - headers.Add("X-Request-ID", fmt.Sprintf("%d/%s", payload.Slot(), payload.BlockHash())) + headers.Add("X-Request-ID", fmt.Sprintf("%d/%s", submission.Slot, submission.BlockHash.String())) if isHighPrio { headers.Add("X-High-Priority", "true") } diff --git a/services/api/optimistic_test.go b/services/api/optimistic_test.go index b0f6e491..6cdcc489 100644 --- a/services/api/optimistic_test.go +++ b/services/api/optimistic_test.go @@ -144,7 +144,7 @@ func runOptimisticBlockSubmission(t *testing.T, opts blockRequestOpts, simErr er } req := common.TestBuilderSubmitBlockRequest(opts.secretkey, getTestBidTrace(opts.pubkey, opts.blockValue)) - rr := backend.request(http.MethodPost, pathSubmitNewBlock, &req) + rr := backend.request(http.MethodPost, pathSubmitNewBlock, req.Capella) // Let updates happen async. time.Sleep(100 * time.Millisecond) @@ -189,7 +189,7 @@ func TestSimulateBlock(t *testing.T) { }, }, req: &common.BuilderBlockValidationRequest{ - BuilderSubmitBlockRequest: common.TestBuilderSubmitBlockRequest( + VersionedSubmitBlockRequest: common.TestBuilderSubmitBlockRequest( secretkey, getTestBidTrace(*pubkey, collateral)), }, }) @@ -239,7 +239,7 @@ func TestProcessOptimisticBlock(t *testing.T) { }, }, req: &common.BuilderBlockValidationRequest{ - BuilderSubmitBlockRequest: common.TestBuilderSubmitBlockRequest( + VersionedSubmitBlockRequest: common.TestBuilderSubmitBlockRequest( secretkey, getTestBidTrace(*pubkey, collateral)), }, }, simResultC) diff --git a/services/api/service.go b/services/api/service.go index d109fec7..ba30a2d0 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -22,8 +22,10 @@ import ( "github.com/NYTimes/gziphandler" builderCapella "github.com/attestantio/go-builder-client/api/capella" + "github.com/attestantio/go-builder-client/spec" consensusapi "github.com/attestantio/go-eth2-client/api" "github.com/attestantio/go-eth2-client/api/v1/capella" + consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/buger/jsonparser" "github.com/flashbots/go-boost-utils/bls" @@ -577,7 +579,7 @@ func (api *RelayAPI) simulateBlock(ctx context.Context, opts blockSimOptions) (r return nil, nil } -func (api *RelayAPI) demoteBuilder(pubkey string, req *common.BuilderSubmitBlockRequest, simError error) { +func (api *RelayAPI) demoteBuilder(pubkey string, req *spec.VersionedSubmitBlockRequest, simError error) { builderEntry, ok := api.blockBuildersCache[pubkey] if !ok { api.log.Warnf("builder %v not in the builder cache", pubkey) @@ -594,10 +596,14 @@ func (api *RelayAPI) demoteBuilder(pubkey string, req *common.BuilderSubmitBlock } // Write to demotions table. api.log.WithFields(logrus.Fields{"builder_pubkey": pubkey}).Info("demoting builder") + bidTrace, err := req.BidTrace() + if err != nil { + api.log.WithError(err).Warn("failed to get bid trace from submit block request") + } if err := api.db.InsertBuilderDemotion(req, simError); err != nil { api.log.WithError(err).WithFields(logrus.Fields{ "errorWritingDemotionToDB": true, - "bidTrace": req.Message, + "bidTrace": bidTrace, "simError": simError, }).Error("failed to save demotion to database") } @@ -612,14 +618,19 @@ func (api *RelayAPI) processOptimisticBlock(opts blockSimOptions, simResultC cha defer api.optimisticBlocksWG.Done() ctx := context.Background() - builderPubkey := opts.req.BuilderPubkey().String() + submission, err := common.GetBlockSubmissionInfo(&opts.req.VersionedSubmitBlockRequest) + if err != nil { + opts.log.WithError(err).Error("error getting block submission info") + return + } + builderPubkey := submission.Builder.String() opts.log.WithFields(logrus.Fields{ "builderPubkey": builderPubkey, // NOTE: this value is just an estimate because many goroutines could be // updating api.optimisticBlocksInFlight concurrently. Since we just use // it for logging, it is not atomic to avoid the performance impact. "optBlocksInFlight": api.optimisticBlocksInFlight, - }).Infof("simulating optimistic block with hash: %v", opts.req.BuilderSubmitBlockRequest.BlockHash()) + }).Infof("simulating optimistic block with hash: %v", submission.BlockHash.String()) reqErr, simErr := api.simulateBlock(ctx, opts) simResultC <- &blockSimResult{reqErr == nil, true, reqErr, simErr} if reqErr != nil || simErr != nil { @@ -635,7 +646,7 @@ func (api *RelayAPI) processOptimisticBlock(opts blockSimOptions, simResultC cha } // Demote the builder. - api.demoteBuilder(builderPubkey, &opts.req.BuilderSubmitBlockRequest, demotionErr) + api.demoteBuilder(builderPubkey, &opts.req.VersionedSubmitBlockRequest, demotionErr) } } @@ -1520,18 +1531,18 @@ func (api *RelayAPI) handleBuilderGetValidators(w http.ResponseWriter, req *http } } -func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *logrus.Entry, payload *common.BuilderSubmitBlockRequest) (uint64, bool) { +func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *logrus.Entry, submission *common.BlockSubmissionInfo) (uint64, bool) { api.proposerDutiesLock.RLock() - slotDuty := api.proposerDutiesMap[payload.Slot()] + slotDuty := api.proposerDutiesMap[submission.Slot] api.proposerDutiesLock.RUnlock() if slotDuty == nil { log.Warn("could not find slot duty") api.RespondError(w, http.StatusBadRequest, "could not find slot duty") return 0, false - } else if !strings.EqualFold(slotDuty.Entry.Message.FeeRecipient.String(), payload.ProposerFeeRecipient()) { + } else if !strings.EqualFold(slotDuty.Entry.Message.FeeRecipient.String(), submission.ProposerFeeRecipient.String()) { log.WithFields(logrus.Fields{ "expectedFeeRecipient": slotDuty.Entry.Message.FeeRecipient.String(), - "actualFeeRecipient": payload.ProposerFeeRecipient(), + "actualFeeRecipient": submission.ProposerFeeRecipient.String(), }).Info("fee recipient does not match") api.RespondError(w, http.StatusBadRequest, "fee recipient does not match") return 0, false @@ -1539,25 +1550,25 @@ 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, submission *common.BlockSubmissionInfo) bool { api.payloadAttributesLock.RLock() - attrs, ok := api.payloadAttributes[payload.ParentHash()] + attrs, ok := api.payloadAttributes[submission.ParentHash.String()] api.payloadAttributesLock.RUnlock() - if !ok || payload.Slot() != attrs.slot { + if !ok || submission.Slot != attrs.slot { log.Warn("payload attributes not (yet) known") api.RespondError(w, http.StatusBadRequest, "payload attributes not (yet) known") return false } - if payload.Random() != attrs.payloadAttributes.PrevRandao { - msg := fmt.Sprintf("incorrect prev_randao - got: %s, expected: %s", payload.Random(), attrs.payloadAttributes.PrevRandao) + if submission.PrevRandao.String() != attrs.payloadAttributes.PrevRandao { + msg := fmt.Sprintf("incorrect prev_randao - got: %s, expected: %s", submission.PrevRandao.String(), attrs.payloadAttributes.PrevRandao) log.Info(msg) api.RespondError(w, http.StatusBadRequest, msg) return false } - if hasReachedFork(payload.Slot(), api.capellaEpoch) { // Capella requires correct withdrawals - withdrawalsRoot, err := ComputeWithdrawalsRoot(payload.Withdrawals()) + if hasReachedFork(submission.Slot, api.capellaEpoch) { // Capella requires correct withdrawals + withdrawalsRoot, err := ComputeWithdrawalsRoot(submission.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") @@ -1575,25 +1586,18 @@ func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *log return true } -func (api *RelayAPI) checkSubmissionSlotDetails(w http.ResponseWriter, log *logrus.Entry, headSlot uint64, payload *common.BuilderSubmitBlockRequest) bool { - // TODO: add deneb support. - if payload.Capella == nil { - log.Info("rejecting submission - non-capella payload for capella fork") - api.RespondError(w, http.StatusBadRequest, "non-capella payload") - return false - } - - if payload.Slot() <= headSlot { +func (api *RelayAPI) checkSubmissionSlotDetails(w http.ResponseWriter, log *logrus.Entry, headSlot uint64, submission *common.BlockSubmissionInfo) bool { + if submission.Slot <= headSlot { log.Info("submitNewBlock failed: submission for past slot") api.RespondError(w, http.StatusBadRequest, "submission for past slot") return false } // Timestamp check - expectedTimestamp := api.genesisInfo.Data.GenesisTime + (payload.Slot() * common.SecondsPerSlot) - if payload.Timestamp() != expectedTimestamp { - log.Warnf("incorrect timestamp. got %d, expected %d", payload.Timestamp(), expectedTimestamp) - api.RespondError(w, http.StatusBadRequest, fmt.Sprintf("incorrect timestamp. got %d, expected %d", payload.Timestamp(), expectedTimestamp)) + expectedTimestamp := api.genesisInfo.Data.GenesisTime + (submission.Slot * common.SecondsPerSlot) + if submission.Timestamp != expectedTimestamp { + log.Warnf("incorrect timestamp. got %d, expected %d", submission.Timestamp, expectedTimestamp) + api.RespondError(w, http.StatusBadRequest, fmt.Sprintf("incorrect timestamp. got %d, expected %d", submission.Timestamp, expectedTimestamp)) return false } @@ -1785,18 +1789,20 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque return } - payload := new(common.BuilderSubmitBlockRequest) + payload := &spec.VersionedSubmitBlockRequest{ //nolint:exhaustruct + Version: consensusspec.DataVersionCapella, + } + payload.Capella = new(builderCapella.SubmitBlockRequest) // Check for SSZ encoding contentType := req.Header.Get("Content-Type") if contentType == "application/octet-stream" { log = log.WithField("reqContentType", "ssz") - payload.Capella = new(builderCapella.SubmitBlockRequest) if err = payload.Capella.UnmarshalSSZ(requestPayloadBytes); err != nil { log.WithError(err).Warn("could not decode payload - SSZ") // SSZ decoding failed. try JSON as fallback (some builders used octet-stream for json before) - if err2 := json.Unmarshal(requestPayloadBytes, payload); err2 != nil { + if err2 := json.Unmarshal(requestPayloadBytes, payload.Capella); err2 != nil { log.WithError(fmt.Errorf("%w / %w", err, err2)).Warn("could not decode payload - SSZ or JSON") api.RespondError(w, http.StatusBadRequest, err.Error()) return @@ -1807,7 +1813,7 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque } } else { log = log.WithField("reqContentType", "json") - if err := json.Unmarshal(requestPayloadBytes, payload); err != nil { + if err := json.Unmarshal(requestPayloadBytes, payload.Capella); err != nil { log.WithError(err).Warn("could not decode payload - JSON") api.RespondError(w, http.StatusBadRequest, err.Error()) return @@ -1819,30 +1825,32 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque prevTime = nextTime isLargeRequest := len(requestPayloadBytes) > fastTrackPayloadSizeLimit + // getting block submission info also validates bid trace and execution submission are not empty + submission, err := common.GetBlockSubmissionInfo(payload) + if err != nil { + log.WithError(err).Warn("missing fields in submit block request") + api.RespondError(w, http.StatusBadRequest, err.Error()) + return + } log = log.WithFields(logrus.Fields{ "timestampAfterDecoding": time.Now().UTC().UnixMilli(), - "slot": payload.Slot(), - "builderPubkey": payload.BuilderPubkey().String(), - "blockHash": payload.BlockHash(), - "proposerPubkey": payload.ProposerPubkey(), - "parentHash": payload.ParentHash(), - "value": payload.Value().String(), - "numTx": payload.NumTx(), + "slot": submission.Slot, + "builderPubkey": submission.Builder.String(), + "blockHash": submission.BlockHash.String(), + "proposerPubkey": submission.Proposer.String(), + "parentHash": submission.ParentHash.String(), + "value": submission.Value.Dec(), + "numTx": len(submission.Transactions), "payloadBytes": len(requestPayloadBytes), "isLargeRequest": isLargeRequest, }) - if payload.Message() == nil || !payload.HasExecutionPayload() { - api.RespondError(w, http.StatusBadRequest, "missing parts of the payload") - return - } - - ok := api.checkSubmissionSlotDetails(w, log, headSlot, payload) + ok := api.checkSubmissionSlotDetails(w, log, headSlot, submission) if !ok { return } - builderPubkey := payload.BuilderPubkey() + builderPubkey := submission.Builder builderEntry, ok := api.checkBuilderEntry(w, log, builderPubkey) if !ok { return @@ -1853,13 +1861,13 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque "timestampAfterChecks1": time.Now().UTC().UnixMilli(), }) - gasLimit, ok := api.checkSubmissionFeeRecipient(w, log, payload) + gasLimit, ok := api.checkSubmissionFeeRecipient(w, log, submission) if !ok { return } // Don't accept blocks with 0 value - if payload.Value().Cmp(ZeroU256.BigInt()) == 0 || payload.NumTx() == 0 { + if submission.Value.ToBig().Cmp(ZeroU256.BigInt()) == 0 || len(submission.Transactions) == 0 { log.Info("submitNewBlock failed: block with 0 value or no txs") w.WriteHeader(http.StatusOK) return @@ -1875,15 +1883,15 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque log = log.WithField("timestampBeforeAttributesCheck", time.Now().UTC().UnixMilli()) - ok = api.checkSubmissionPayloadAttrs(w, log, payload) + ok = api.checkSubmissionPayloadAttrs(w, log, submission) if !ok { return } // Verify the signature log = log.WithField("timestampBeforeSignatureCheck", time.Now().UTC().UnixMilli()) - signature := payload.Signature() - ok, err = boostTypes.VerifySignature(payload.Message(), api.opts.EthNetDetails.DomainBuilder, builderPubkey[:], signature[:]) + signature := submission.Signature + ok, err = boostTypes.VerifySignature(submission.BidTrace, api.opts.EthNetDetails.DomainBuilder, builderPubkey[:], signature[:]) log = log.WithField("timestampAfterSignatureCheck", time.Now().UTC().UnixMilli()) if err != nil { log.WithError(err).Warn("failed verifying builder signature") @@ -1944,11 +1952,11 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque // Get the latest top bid value from Redis bidIsTopBid := false - topBidValue, err := api.redis.GetTopBidValue(context.Background(), tx, payload.Slot(), payload.ParentHash(), payload.ProposerPubkey()) + topBidValue, err := api.redis.GetTopBidValue(context.Background(), tx, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) if err != nil { log.WithError(err).Error("failed to get top bid value from redis") } else { - bidIsTopBid = payload.Value().Cmp(topBidValue) == 1 + bidIsTopBid = submission.Value.ToBig().Cmp(topBidValue) == 1 log = log.WithFields(logrus.Fields{ "topBidValue": topBidValue.String(), "newBidIsTopBid": bidIsTopBid, @@ -1975,14 +1983,14 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque log: log, builder: builderEntry, req: &common.BuilderBlockValidationRequest{ - BuilderSubmitBlockRequest: *payload, + VersionedSubmitBlockRequest: *payload, RegisteredGasLimit: gasLimit, }, } // With sufficient collateral, process the block optimistically. if builderEntry.status.IsOptimistic && - builderEntry.collateral.Cmp(payload.Value()) >= 0 && - payload.Slot() == api.optimisticSlot.Load() { + builderEntry.collateral.Cmp(submission.Value.ToBig()) >= 0 && + submission.Slot == api.optimisticSlot.Load() { go api.processOptimisticBlock(opts, simResultC) } else { // Simulate block (synchronously). @@ -2023,7 +2031,7 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque // latency will make it impossible to predict which arrives first. Thus a high bid could unintentionally be overwritten by a low bid that happened // to arrive a few microseconds later. If builders are submitting blocks at a frequency where they cannot reliably predict which bid will arrive at // the relay first, they should instead use multiple pubkeys to avoid uninitentionally overwriting their own bids. - latestPayloadReceivedAt, err := api.redis.GetBuilderLatestPayloadReceivedAt(context.Background(), tx, payload.Slot(), payload.BuilderPubkey().String(), payload.ParentHash(), payload.ProposerPubkey()) + latestPayloadReceivedAt, err := api.redis.GetBuilderLatestPayloadReceivedAt(context.Background(), tx, submission.Slot, submission.Builder.String(), submission.ParentHash.String(), submission.Proposer.String()) if err != nil { log.WithError(err).Error("failed getting latest payload receivedAt from redis") } else if receivedAt.UnixMilli() < latestPayloadReceivedAt { @@ -2067,7 +2075,7 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque // Save to memcache in the background if api.memcached != nil { go func() { - err = api.memcached.SaveExecutionPayload(payload.Slot(), payload.ProposerPubkey(), payload.BlockHash(), getPayloadResponse) + err = api.memcached.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), getPayloadResponse) if err != nil { log.WithError(err).Error("failed saving execution payload in memcached") } diff --git a/services/api/service_test.go b/services/api/service_test.go index 8c115e29..0a0a8d0a 100644 --- a/services/api/service_test.go +++ b/services/api/service_test.go @@ -14,10 +14,12 @@ import ( "github.com/alicebob/miniredis/v2" builderCapella "github.com/attestantio/go-builder-client/api/capella" v1 "github.com/attestantio/go-builder-client/api/v1" + "github.com/attestantio/go-builder-client/spec" + consensusspec "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" - "github.com/attestantio/go-builder-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/flashbots/go-boost-utils/bls" "github.com/flashbots/go-boost-utils/types" "github.com/flashbots/mev-boost-relay/beaconclient" @@ -397,8 +399,9 @@ func TestDataApiGetDataProposerPayloadDelivered(t *testing.T) { func TestBuilderSubmitBlockSSZ(t *testing.T) { requestPayloadJSONBytes := common.LoadGzippedBytes(t, "../../testdata/submitBlockPayloadCapella_Goerli.json.gz") - req := new(common.BuilderSubmitBlockRequest) - err := json.Unmarshal(requestPayloadJSONBytes, &req) + req := new(spec.VersionedSubmitBlockRequest) + req.Capella = new(builderCapella.SubmitBlockRequest) + err := json.Unmarshal(requestPayloadJSONBytes, req.Capella) require.NoError(t, err) reqSSZ, err := req.Capella.MarshalSSZ() @@ -452,10 +455,11 @@ func TestBuilderSubmitBlock(t *testing.T) { } // Prepare the request payload - req := new(common.BuilderSubmitBlockRequest) + req := new(spec.VersionedSubmitBlockRequest) + req.Capella = new(builderCapella.SubmitBlockRequest) requestPayloadJSONBytes := common.LoadGzippedBytes(t, payloadJSONFilename) require.NoError(t, err) - err = json.Unmarshal(requestPayloadJSONBytes, &req) + err = json.Unmarshal(requestPayloadJSONBytes, req.Capella) require.NoError(t, err) // Update @@ -510,7 +514,7 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { cases := []struct { description string slotDuty *common.BuilderGetValidatorsResponseEntry - payload *common.BuilderSubmitBlockRequest + payload *spec.VersionedSubmitBlockRequest expectOk bool expectGasLimit uint64 }{ @@ -524,7 +528,8 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { }, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ + Version: consensusspec.DataVersionCapella, Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot, @@ -538,7 +543,8 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { { description: "failure_nil_slot_duty", slotDuty: nil, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ + Version: consensusspec.DataVersionCapella, Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot, @@ -558,7 +564,8 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { }, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ + Version: consensusspec.DataVersionCapella, Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot, @@ -574,13 +581,17 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { t.Run(tc.description, func(t *testing.T) { _, _, backend := startTestBackend(t) backend.relay.proposerDutiesLock.RLock() - backend.relay.proposerDutiesMap[tc.payload.Slot()] = tc.slotDuty + slot, err := tc.payload.Slot() + require.NoError(t, err) + backend.relay.proposerDutiesMap[slot] = tc.slotDuty backend.relay.proposerDutiesLock.RUnlock() w := httptest.NewRecorder() logger := logrus.New() log := logrus.NewEntry(logger) - gasLimit, ok := backend.relay.checkSubmissionFeeRecipient(w, log, tc.payload) + submission, err := common.GetBlockSubmissionInfo(tc.payload) + require.NoError(t, err) + gasLimit, ok := backend.relay.checkSubmissionFeeRecipient(w, log, submission) require.Equal(t, tc.expectGasLimit, gasLimit) require.Equal(t, tc.expectOk, ok) }) @@ -598,7 +609,7 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { cases := []struct { description string attrs payloadAttributesHelper - payload *common.BuilderSubmitBlockRequest + payload *spec.VersionedSubmitBlockRequest expectOk bool }{ { @@ -606,12 +617,13 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { attrs: payloadAttributesHelper{ slot: testSlot, parentHash: testParentHash, - withdrawalsRoot: phase0.Root(withdrawalsRoot), + withdrawalsRoot: withdrawalsRoot, payloadAttributes: beaconclient.PayloadAttributes{ PrevRandao: testPrevRandao, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ + Version: consensusspec.DataVersionCapella, Capella: &builderCapella.SubmitBlockRequest{ ExecutionPayload: &capella.ExecutionPayload{ PrevRandao: [32]byte(prevRandao), @@ -634,7 +646,7 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { attrs: payloadAttributesHelper{ slot: testSlot, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot + 1, // submission for a future slot @@ -651,7 +663,7 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { PrevRandao: testPrevRandao, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot, @@ -672,7 +684,7 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { PrevRandao: testPrevRandao, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot, @@ -696,7 +708,7 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { PrevRandao: testPrevRandao, }, }, - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ ExecutionPayload: &capella.ExecutionPayload{ PrevRandao: [32]byte(prevRandao), @@ -725,7 +737,9 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { w := httptest.NewRecorder() logger := logrus.New() log := logrus.NewEntry(logger) - ok := backend.relay.checkSubmissionPayloadAttrs(w, log, tc.payload) + submission, err := common.GetBlockSubmissionInfo(tc.payload) + require.NoError(t, err) + ok := backend.relay.checkSubmissionPayloadAttrs(w, log, submission) require.Equal(t, tc.expectOk, ok) }) } @@ -734,12 +748,12 @@ func TestCheckSubmissionPayloadAttrs(t *testing.T) { func TestCheckSubmissionSlotDetails(t *testing.T) { cases := []struct { description string - payload *common.BuilderSubmitBlockRequest - expectOk bool + payload *spec.VersionedSubmitBlockRequest + expectOk bool }{ { description: "success", - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ ExecutionPayload: &capella.ExecutionPayload{ Timestamp: testSlot * common.SecondsPerSlot, @@ -753,14 +767,14 @@ func TestCheckSubmissionSlotDetails(t *testing.T) { }, { description: "failure_nil_capella", - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: nil, // nil to cause error }, expectOk: false, }, { description: "failure_past_slot", - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ Message: &v1.BidTrace{ Slot: testSlot - 1, // use old slot to cause error @@ -771,7 +785,7 @@ func TestCheckSubmissionSlotDetails(t *testing.T) { }, { description: "failure_wrong_timestamp", - payload: &common.BuilderSubmitBlockRequest{ + payload: &spec.VersionedSubmitBlockRequest{ Capella: &builderCapella.SubmitBlockRequest{ ExecutionPayload: &capella.ExecutionPayload{ Timestamp: testSlot*common.SecondsPerSlot - 1, // use wrong timestamp to cause error @@ -792,7 +806,9 @@ func TestCheckSubmissionSlotDetails(t *testing.T) { w := httptest.NewRecorder() logger := logrus.New() log := logrus.NewEntry(logger) - ok := backend.relay.checkSubmissionSlotDetails(w, log, headSlot, tc.payload) + submission, err := common.GetBlockSubmissionInfo(tc.payload) + require.NoError(t, err) + ok := backend.relay.checkSubmissionSlotDetails(w, log, headSlot, submission) require.Equal(t, tc.expectOk, ok) }) } diff --git a/services/api/utils.go b/services/api/utils.go index a76e41da..e4655d0f 100644 --- a/services/api/utils.go +++ b/services/api/utils.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/attestantio/go-builder-client/api" + "github.com/attestantio/go-builder-client/spec" consensusapi "github.com/attestantio/go-eth2-client/api" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -24,12 +25,16 @@ var ( ErrHeaderHTRMismatch = errors.New("beacon-block and payload header mismatch") ) -func SanityCheckBuilderBlockSubmission(payload *common.BuilderSubmitBlockRequest) error { - if payload.BlockHash() != payload.ExecutionPayloadBlockHash() { +func SanityCheckBuilderBlockSubmission(payload *spec.VersionedSubmitBlockRequest) error { + submission, err := common.GetBlockSubmissionInfo(payload) + if err != nil { + return err + } + if submission.BlockHash.String() != submission.ExecutionPayloadBlockHash.String() { return ErrBlockHashMismatch } - if payload.ParentHash() != payload.ExecutionPayloadParentHash() { + if submission.ParentHash.String() != submission.ExecutionPayloadParentHash.String() { return ErrParentHashMismatch }