diff --git a/common/test_utils.go b/common/test_utils.go index 4f49d2b6..410f0000 100644 --- a/common/test_utils.go +++ b/common/test_utils.go @@ -78,7 +78,7 @@ var ValidPayloadRegisterValidator = builderApiV1.SignedValidatorRegistration{ "0xaf12df007a0c78abb5575067e5f8b089cfcc6227e4a91db7dd8cf517fe86fb944ead859f0781277d9b78c672e4a18c5d06368b603374673cf2007966cece9540f3a1b3f6f9e1bf421d779c4e8010368e6aac134649c7a009210780d401a778a5"), } -func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2, version spec.DataVersion) *VersionedSubmitBlockRequest { +func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2WithBlobFields, version spec.DataVersion) *VersionedSubmitBlockRequest { signature, err := ssz.SignMessage(bid, ssz.DomainBuilder, sk) check(err, " SignMessage: ", bid, sk) if version == spec.DataVersionDeneb { diff --git a/common/types.go b/common/types.go index e1973b96..ee359495 100644 --- a/common/types.go +++ b/common/types.go @@ -10,10 +10,10 @@ import ( builderApiV1 "github.com/attestantio/go-builder-client/api/v1" "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" ssz "github.com/ferranbt/fastssz" boostSsz "github.com/flashbots/go-boost-utils/ssz" - "github.com/holiman/uint256" ) var ( @@ -194,8 +194,8 @@ type BuilderGetValidatorsResponseEntry struct { type BidTraceV2 struct { builderApiV1.BidTrace - BlockNumber uint64 `db:"block_number" json:"block_number,string"` - NumTx uint64 `db:"num_tx" json:"num_tx,string"` + BlockNumber uint64 `db:"block_number" json:"block_number,string"` + NumTx uint64 `db:"num_tx" json:"num_tx,string"` } type BidTraceV2JSON struct { @@ -326,25 +326,93 @@ func (b *BidTraceV2WithTimestampJSON) ToCSVRecord() []string { } } +type BidTraceV2WithBlobFields struct { + builderApiV1.BidTrace + BlockNumber uint64 `db:"block_number" json:"block_number,string"` + NumTx uint64 `db:"num_tx" json:"num_tx,string"` + NumBlobs uint64 `db:"num_blobs" json:"num_blobs,string"` + BlobGasUsed uint64 `db:"blob_gas_used" json:"blob_gas_used,string"` + ExcessBlobGas uint64 `db:"excess_blob_gas" json:"excess_blob_gas,string"` +} + +type BidTraceV2WithBlobFieldsJSON struct { + Slot uint64 `json:"slot,string"` + ParentHash string `json:"parent_hash"` + BlockHash string `json:"block_hash"` + BuilderPubkey string `json:"builder_pubkey"` + ProposerPubkey string `json:"proposer_pubkey"` + ProposerFeeRecipient string `json:"proposer_fee_recipient"` + GasLimit uint64 `json:"gas_limit,string"` + GasUsed uint64 `json:"gas_used,string"` + Value string `json:"value"` + NumTx uint64 `json:"num_tx,string"` + BlockNumber uint64 `json:"block_number,string"` + NumBlobs uint64 `json:"num_blobs,string"` + BlobGasUsed uint64 `json:"blob_gas_used,string"` + ExcessBlobGas uint64 `json:"excess_blob_gas,string"` +} + +func (b BidTraceV2WithBlobFields) MarshalJSON() ([]byte, error) { + return json.Marshal(&BidTraceV2WithBlobFieldsJSON{ + Slot: b.Slot, + ParentHash: b.ParentHash.String(), + BlockHash: b.BlockHash.String(), + BuilderPubkey: b.BuilderPubkey.String(), + ProposerPubkey: b.ProposerPubkey.String(), + ProposerFeeRecipient: b.ProposerFeeRecipient.String(), + GasLimit: b.GasLimit, + GasUsed: b.GasUsed, + Value: b.Value.ToBig().String(), + NumTx: b.NumTx, + BlockNumber: b.BlockNumber, + NumBlobs: b.NumBlobs, + BlobGasUsed: b.BlobGasUsed, + ExcessBlobGas: b.ExcessBlobGas, + }) +} + +func (b *BidTraceV2WithBlobFields) UnmarshalJSON(data []byte) error { + params := &struct { + NumTx uint64 `json:"num_tx,string"` + BlockNumber uint64 `json:"block_number,string"` + NumBlobs uint64 `json:"num_blobs,string"` + BlobGasUsed uint64 `json:"blob_gas_used,string"` + ExcessBlobGas uint64 `json:"excess_blob_gas,string"` + }{} + err := json.Unmarshal(data, params) + if err != nil { + return err + } + b.NumTx = params.NumTx + b.BlockNumber = params.BlockNumber + b.NumBlobs = params.NumBlobs + b.BlobGasUsed = params.BlobGasUsed + b.ExcessBlobGas = params.ExcessBlobGas + + bidTrace := new(builderApiV1.BidTrace) + err = json.Unmarshal(data, bidTrace) + if err != nil { + return err + } + b.BidTrace = *bidTrace + return nil +} + type BlockSubmissionInfo struct { BidTrace *builderApiV1.BidTrace - Slot uint64 - BlockHash phase0.Hash32 - ParentHash phase0.Hash32 ExecutionPayloadBlockHash phase0.Hash32 ExecutionPayloadParentHash phase0.Hash32 - Builder phase0.BLSPubKey - Proposer phase0.BLSPubKey - ProposerFeeRecipient bellatrix.ExecutionAddress GasUsed uint64 GasLimit uint64 Timestamp uint64 BlockNumber uint64 - Value *uint256.Int PrevRandao phase0.Hash32 Signature phase0.BLSSignature Transactions []bellatrix.Transaction Withdrawals []*capella.Withdrawal + Blobs []deneb.Blob + BlobGasUsed uint64 + ExcessBlobGas uint64 } /* diff --git a/common/types_spec.go b/common/types_spec.go index eb854954..1e3ae491 100644 --- a/common/types_spec.go +++ b/common/types_spec.go @@ -7,6 +7,7 @@ import ( builderApi "github.com/attestantio/go-builder-client/api" builderApiCapella "github.com/attestantio/go-builder-client/api/capella" builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" + builderApiV1 "github.com/attestantio/go-builder-client/api/v1" builderSpec "github.com/attestantio/go-builder-client/spec" eth2Api "github.com/attestantio/go-eth2-client/api" eth2ApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella" @@ -232,28 +233,47 @@ func DenebUnblindSignedBlock(blindedBlock *eth2ApiV1Deneb.SignedBlindedBeaconBlo type BuilderBlockValidationRequest struct { *VersionedSubmitBlockRequest - RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` - ParentBeaconBlockRoot *phase0.Root `json:"parent_beacon_block_root,omitempty"` + RegisteredGasLimit uint64 + ParentBeaconBlockRoot *phase0.Root } -func (r *BuilderBlockValidationRequest) MarshalJSON() ([]byte, error) { - blockRequest, err := json.Marshal(r.VersionedSubmitBlockRequest) - if err != nil { - return nil, err - } +type capellaBuilderBlockValidationRequestJSON struct { + Message *builderApiV1.BidTrace `json:"message"` + ExecutionPayload *capella.ExecutionPayload `json:"execution_payload"` + Signature string `json:"signature"` + RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` +} - attrs, err := json.Marshal(&struct { - RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` - ParentBeaconBlockRoot *phase0.Root `json:"parent_beacon_block_root,omitempty"` - }{ - RegisteredGasLimit: r.RegisteredGasLimit, - ParentBeaconBlockRoot: r.ParentBeaconBlockRoot, - }) - if err != nil { - return nil, err +type denebBuilderBlockValidationRequestJSON struct { + Message *builderApiV1.BidTrace `json:"message"` + ExecutionPayload *deneb.ExecutionPayload `json:"execution_payload"` + BlobsBundle *builderApiDeneb.BlobsBundle `json:"blobs_bundle"` + Signature string `json:"signature"` + RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` + ParentBeaconBlockRoot string `json:"parent_beacon_block_root"` +} + +func (r *BuilderBlockValidationRequest) MarshalJSON() ([]byte, error) { + switch r.Version { //nolint:exhaustive + case spec.DataVersionCapella: + return json.Marshal(&capellaBuilderBlockValidationRequestJSON{ + Message: r.Capella.Message, + ExecutionPayload: r.Capella.ExecutionPayload, + Signature: r.Capella.Signature.String(), + RegisteredGasLimit: r.RegisteredGasLimit, + }) + case spec.DataVersionDeneb: + return json.Marshal(&denebBuilderBlockValidationRequestJSON{ + Message: r.Deneb.Message, + ExecutionPayload: r.Deneb.ExecutionPayload, + BlobsBundle: r.Deneb.BlobsBundle, + Signature: r.Deneb.Signature.String(), + RegisteredGasLimit: r.RegisteredGasLimit, + ParentBeaconBlockRoot: r.ParentBeaconBlockRoot.String(), + }) + default: + return nil, errors.Wrap(ErrInvalidVersion, fmt.Sprintf("%s is not supported", r.Version)) } - attrs[0] = ',' - return append(blockRequest[:len(blockRequest)-1], attrs...), nil } type VersionedSubmitBlockRequest struct { diff --git a/common/utils.go b/common/utils.go index 257d54e9..8f48c45c 100644 --- a/common/utils.go +++ b/common/utils.go @@ -17,6 +17,7 @@ import ( builderApi "github.com/attestantio/go-builder-client/api" builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -190,18 +191,6 @@ func GetBlockSubmissionInfo(submission *VersionedSubmitBlockRequest) (*BlockSubm 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 @@ -210,18 +199,6 @@ func GetBlockSubmissionInfo(submission *VersionedSubmitBlockRequest) (*BlockSubm 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 @@ -238,10 +215,6 @@ func GetBlockSubmissionInfo(submission *VersionedSubmitBlockRequest) (*BlockSubm 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 @@ -254,25 +227,32 @@ func GetBlockSubmissionInfo(submission *VersionedSubmitBlockRequest) (*BlockSubm if err != nil { return nil, err } + // TODO (deneb): after deneb fork error if no blob fields + var ( + blobs []deneb.Blob + blobGasUsed uint64 + excessBlobGas uint64 + ) + if submission.Version == spec.DataVersionDeneb { + blobs = submission.Deneb.BlobsBundle.Blobs + blobGasUsed = submission.Deneb.ExecutionPayload.BlobGasUsed + excessBlobGas = submission.Deneb.ExecutionPayload.ExcessBlobGas + } 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, + Blobs: blobs, + BlobGasUsed: blobGasUsed, + ExcessBlobGas: excessBlobGas, }, nil } diff --git a/database/database.go b/database/database.go index 45627995..704d8a08 100644 --- a/database/database.go +++ b/database/database.go @@ -34,7 +34,7 @@ type IDatabaseService interface { GetExecutionPayloads(idFirst, idLast uint64) (entries []*ExecutionPayloadEntry, err error) DeleteExecutionPayloads(idFirst, idLast uint64) error - SaveDeliveredPayload(bidTrace *common.BidTraceV2, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error + SaveDeliveredPayload(bidTrace *common.BidTraceV2WithBlobFields, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error GetNumDeliveredPayloads() (uint64, error) GetRecentDeliveredPayloads(filters GetPayloadsFilters) ([]*DeliveredPayloadEntry, error) GetDeliveredPayloads(idFirst, idLast uint64) (entries []*DeliveredPayloadEntry, err error) @@ -48,8 +48,8 @@ type IDatabaseService interface { IncBlockBuilderStatsAfterGetPayload(builderPubkey string) error InsertBuilderDemotion(submitBlockRequest *common.VersionedSubmitBlockRequest, simError error) error - UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error - GetBuilderDemotion(trace *common.BidTraceV2) (*BuilderDemotionEntry, error) + UpdateBuilderDemotion(trace *common.BidTraceV2WithBlobFields, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error + GetBuilderDemotion(trace *common.BidTraceV2WithBlobFields) (*BuilderDemotionEntry, error) GetTooLateGetPayload(slot uint64) (entries []*TooLateGetPayloadEntry, err error) InsertTooLateGetPayload(slot uint64, proposerPubkey, blockHash string, slotStart, requestTime, decodeTime, msIntoSlot uint64) error @@ -217,21 +217,21 @@ func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.VersionedSu Signature: submission.Signature.String(), - Slot: submission.Slot, - BlockHash: submission.BlockHash.String(), - ParentHash: submission.ParentHash.String(), + Slot: submission.BidTrace.Slot, + BlockHash: submission.BidTrace.BlockHash.String(), + ParentHash: submission.BidTrace.ParentHash.String(), - BuilderPubkey: submission.Builder.String(), - ProposerPubkey: submission.Proposer.String(), - ProposerFeeRecipient: submission.ProposerFeeRecipient.String(), + BuilderPubkey: submission.BidTrace.BuilderPubkey.String(), + ProposerPubkey: submission.BidTrace.ProposerPubkey.String(), + ProposerFeeRecipient: submission.BidTrace.ProposerFeeRecipient.String(), GasUsed: submission.GasUsed, GasLimit: submission.GasLimit, NumTx: uint64(len(submission.Transactions)), - Value: submission.Value.Dec(), + Value: submission.BidTrace.Value.Dec(), - Epoch: submission.Slot / common.SlotsPerEpoch, + Epoch: submission.BidTrace.Slot / common.SlotsPerEpoch, BlockNumber: submission.BlockNumber, DecodeDuration: profile.Decode, @@ -272,7 +272,7 @@ func (s *DatabaseService) GetExecutionPayloadEntryBySlotPkHash(slot uint64, prop return entry, err } -func (s *DatabaseService) SaveDeliveredPayload(bidTrace *common.BidTraceV2, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error { +func (s *DatabaseService) SaveDeliveredPayload(bidTrace *common.BidTraceV2WithBlobFields, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error { _signedBlindedBeaconBlock, err := json.Marshal(signedBlindedBeaconBlock) if err != nil { return err @@ -299,12 +299,16 @@ func (s *DatabaseService) SaveDeliveredPayload(bidTrace *common.BidTraceV2, sign NumTx: bidTrace.NumTx, Value: bidTrace.Value.ToBig().String(), + NumBlobs: bidTrace.NumBlobs, + BlobGasUsed: bidTrace.BlobGasUsed, + ExcessBlobGas: bidTrace.ExcessBlobGas, + PublishMs: publishMs, } query := `INSERT INTO ` + vars.TableDeliveredPayload + ` - (signed_at, signed_blinded_beacon_block, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, gas_used, gas_limit, num_tx, value, publish_ms) VALUES - (:signed_at, :signed_blinded_beacon_block, :slot, :epoch, :builder_pubkey, :proposer_pubkey, :proposer_fee_recipient, :parent_hash, :block_hash, :block_number, :gas_used, :gas_limit, :num_tx, :value, :publish_ms) + (signed_at, signed_blinded_beacon_block, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, gas_used, gas_limit, num_tx, value, num_blobs, blob_gas_used, excess_blob_gas, publish_ms) VALUES + (:signed_at, :signed_blinded_beacon_block, :slot, :epoch, :builder_pubkey, :proposer_pubkey, :proposer_fee_recipient, :parent_hash, :block_hash, :block_number, :gas_used, :gas_limit, :num_tx, :value, :num_blobs, :blob_gas_used, :excess_blob_gas, :publish_ms) ON CONFLICT DO NOTHING` _, err = s.DB.NamedExec(query, deliveredPayloadEntry) return err @@ -321,7 +325,7 @@ func (s *DatabaseService) GetRecentDeliveredPayloads(queryArgs GetPayloadsFilter "builder_pubkey": queryArgs.BuilderPubkey, } - fields := "id, inserted_at, signed_at, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, num_tx, value, gas_used, gas_limit, publish_ms" + fields := "id, inserted_at, signed_at, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, num_tx, value, num_blobs, blob_gas_used, excess_blob_gas, gas_used, gas_limit, publish_ms" whereConds := []string{} if queryArgs.Slot > 0 { @@ -375,7 +379,7 @@ func (s *DatabaseService) GetRecentDeliveredPayloads(queryArgs GetPayloadsFilter } func (s *DatabaseService) GetDeliveredPayloads(idFirst, idLast uint64) (entries []*DeliveredPayloadEntry, err error) { - query := `SELECT id, inserted_at, signed_at, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, num_tx, value, gas_used, gas_limit, publish_ms + query := `SELECT id, inserted_at, signed_at, slot, epoch, builder_pubkey, proposer_pubkey, proposer_fee_recipient, parent_hash, block_hash, block_number, num_tx, value, num_blobs, blob_gas_used, excess_blob_gas, gas_used, gas_limit, publish_ms FROM ` + vars.TableDeliveredPayload + ` WHERE id >= $1 AND id <= $2 ORDER BY slot ASC` @@ -553,16 +557,16 @@ func (s *DatabaseService) InsertBuilderDemotion(submitBlockRequest *common.Versi builderDemotionEntry := BuilderDemotionEntry{ SubmitBlockRequest: NewNullString(string(_submitBlockRequest)), - Epoch: submission.Slot / common.SlotsPerEpoch, - Slot: submission.Slot, + Epoch: submission.BidTrace.Slot / common.SlotsPerEpoch, + Slot: submission.BidTrace.Slot, - BuilderPubkey: submission.Builder.String(), - ProposerPubkey: submission.Proposer.String(), + BuilderPubkey: submission.BidTrace.BuilderPubkey.String(), + ProposerPubkey: submission.BidTrace.ProposerPubkey.String(), - Value: submission.Value.Dec(), - FeeRecipient: submission.ProposerFeeRecipient.String(), + Value: submission.BidTrace.Value.Dec(), + FeeRecipient: submission.BidTrace.ProposerFeeRecipient.String(), - BlockHash: submission.BlockHash.String(), + BlockHash: submission.BidTrace.BlockHash.String(), SimError: simError.Error(), } @@ -574,7 +578,7 @@ func (s *DatabaseService) InsertBuilderDemotion(submitBlockRequest *common.Versi return err } -func (s *DatabaseService) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error { +func (s *DatabaseService) UpdateBuilderDemotion(trace *common.BidTraceV2WithBlobFields, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error { _signedBeaconBlock, err := json.Marshal(signedBlock) if err != nil { return err @@ -592,7 +596,7 @@ func (s *DatabaseService) UpdateBuilderDemotion(trace *common.BidTraceV2, signed return err } -func (s *DatabaseService) GetBuilderDemotion(trace *common.BidTraceV2) (*BuilderDemotionEntry, error) { +func (s *DatabaseService) GetBuilderDemotion(trace *common.BidTraceV2WithBlobFields) (*BuilderDemotionEntry, error) { query := `SELECT submit_block_request, signed_beacon_block, signed_validator_registration, epoch, slot, builder_pubkey, proposer_pubkey, value, fee_recipient, block_hash, sim_error FROM ` + vars.TableBuilderDemotions + ` WHERE slot=$1 AND builder_pubkey=$2 AND block_hash=$3` entry := &BuilderDemotionEntry{} diff --git a/database/database_test.go b/database/database_test.go index 034111d8..274f4d18 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -77,7 +77,7 @@ func insertTestBuilder(t *testing.T, db IDatabaseService) string { hashSlice, err := hexutil.Decode(blockHashStr) require.NoError(t, err) copy(testBlockHash[:], hashSlice) - req := common.TestBuilderSubmitBlockRequest(sk, &common.BidTraceV2{ + req := common.TestBuilderSubmitBlockRequest(sk, &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ BlockHash: testBlockHash, Slot: slot, @@ -298,7 +298,7 @@ func TestInsertBuilderDemotion(t *testing.T) { hashSlice, err := hexutil.Decode(blockHashStr) require.NoError(t, err) copy(testBlockHash[:], hashSlice) - trace := &common.BidTraceV2{ + trace := &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ BlockHash: testBlockHash, Slot: slot, @@ -344,7 +344,7 @@ func TestUpdateBuilderDemotion(t *testing.T) { hashSlice, err := hexutil.Decode(blockHashStr) require.NoError(t, err) copy(testBlockHash[:], hashSlice) - bt := &common.BidTraceV2{ + bt := &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ BlockHash: testBlockHash, Slot: slot, diff --git a/database/migrations/010_payload_add_blob_fields.go b/database/migrations/010_payload_add_blob_fields.go new file mode 100644 index 00000000..5d76d36e --- /dev/null +++ b/database/migrations/010_payload_add_blob_fields.go @@ -0,0 +1,21 @@ +package migrations + +import ( + "github.com/flashbots/mev-boost-relay/database/vars" + migrate "github.com/rubenv/sql-migrate" +) + +// Migration010PayloadAddBlobFields adds blob related fields for the Dencun fork +// such as the number of blobs, blob gas used and excess blob gas +var Migration010PayloadAddBlobFields = &migrate.Migration{ + Id: "010-payload-add-blob-fields", + Up: []string{` + ALTER TABLE ` + vars.TableDeliveredPayload + ` ADD blob_gas_used bigint NOT NULL DEFAULT 0; + ALTER TABLE ` + vars.TableDeliveredPayload + ` ADD excess_blob_gas bigint NOT NULL DEFAULT 0; + ALTER TABLE ` + vars.TableDeliveredPayload + ` ADD num_blobs int NOT NULL DEFAULT 0; + `}, + Down: []string{}, + + DisableTransactionUp: true, + DisableTransactionDown: true, +} diff --git a/database/migrations/migration.go b/database/migrations/migration.go index 0610fcc9..82b4e1ff 100644 --- a/database/migrations/migration.go +++ b/database/migrations/migration.go @@ -16,5 +16,6 @@ var Migrations = migrate.MemoryMigrationSource{ Migration007BuilderSubmissionWasSimulated, Migration008Optimistic, Migration009BlockBuilderRemoveReference, + Migration010PayloadAddBlobFields, }, } diff --git a/database/mockdb.go b/database/mockdb.go index 42125c91..fe65645e 100644 --- a/database/mockdb.go +++ b/database/mockdb.go @@ -85,7 +85,7 @@ func (db MockDB) GetBuilderSubmissionsBySlots(slotFrom, slotTo uint64) (entries return nil, nil } -func (db MockDB) SaveDeliveredPayload(bidTrace *common.BidTraceV2, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error { +func (db MockDB) SaveDeliveredPayload(bidTrace *common.BidTraceV2WithBlobFields, signedBlindedBeaconBlock *common.VersionedSignedBlindedBeaconBlock, signedAt time.Time, publishMs uint64) error { return nil } @@ -162,7 +162,7 @@ func (db MockDB) InsertBuilderDemotion(submitBlockRequest *common.VersionedSubmi return nil } -func (db MockDB) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error { +func (db MockDB) UpdateBuilderDemotion(trace *common.BidTraceV2WithBlobFields, signedBlock *common.VersionedSignedProposal, signedRegistration *builderApiV1.SignedValidatorRegistration) error { pubkey := trace.BuilderPubkey.String() _, ok := db.Builders[pubkey] if !ok { @@ -175,7 +175,7 @@ func (db MockDB) UpdateBuilderDemotion(trace *common.BidTraceV2, signedBlock *co return nil } -func (db MockDB) GetBuilderDemotion(trace *common.BidTraceV2) (*BuilderDemotionEntry, error) { +func (db MockDB) GetBuilderDemotion(trace *common.BidTraceV2WithBlobFields) (*BuilderDemotionEntry, error) { pubkey := trace.BuilderPubkey.String() _, ok := db.Builders[pubkey] if !ok { diff --git a/database/types.go b/database/types.go index f4353397..313cf171 100644 --- a/database/types.go +++ b/database/types.go @@ -193,6 +193,10 @@ type DeliveredPayloadEntry struct { NumTx uint64 `db:"num_tx"` Value string `db:"value"` + NumBlobs uint64 `db:"num_blobs"` + BlobGasUsed uint64 `db:"blob_gas_used"` + ExcessBlobGas uint64 `db:"excess_blob_gas"` + PublishMs uint64 `db:"publish_ms"` } diff --git a/database/typesconv.go b/database/typesconv.go index 89746601..3725c472 100644 --- a/database/typesconv.go +++ b/database/typesconv.go @@ -44,9 +44,9 @@ func PayloadToExecPayloadEntry(payload *common.VersionedSubmitBlockRequest) (*Ex } return &ExecutionPayloadEntry{ - Slot: submission.Slot, - ProposerPubkey: submission.Proposer.String(), - BlockHash: submission.BlockHash.String(), + Slot: submission.BidTrace.Slot, + ProposerPubkey: submission.BidTrace.ProposerPubkey.String(), + BlockHash: submission.BidTrace.BlockHash.String(), Version: version, Payload: string(_payload), diff --git a/datastore/memcached_test.go b/datastore/memcached_test.go index 877373c0..5a42fcf7 100644 --- a/datastore/memcached_test.go +++ b/datastore/memcached_test.go @@ -223,16 +223,16 @@ func TestMemcached(t *testing.T) { require.NoError(t, err) // key should not exist in cache yet - empty, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + empty, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String()) require.NoError(t, err) require.Nil(t, empty) submission, err = common.GetBlockSubmissionInfo(&tc.Input) require.NoError(t, err) - err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) + err = mem.SaveExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), payload) require.NoError(t, err) - get, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + get, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String()) require.NoError(t, err, "expected no error when fetching execution payload from memcached but found [%v]", err) getBytes, err := get.MarshalJSON() @@ -259,20 +259,20 @@ func TestMemcached(t *testing.T) { submission, err := common.GetBlockSubmissionInfo(&tc.Input) require.NoError(t, err) - err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) + err = mem.SaveExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), payload) require.NoError(t, err) - prev, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + prev, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String()) require.NoError(t, err) require.Equal(t, len(prev.Capella.Transactions), len(submission.Transactions)) payload.Capella.GasLimit++ require.NotEqual(t, prev.Capella.GasLimit, payload.Capella.GasLimit) - err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) + err = mem.SaveExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), payload) require.NoError(t, err) - current, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + current, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String()) require.NoError(t, err) require.Equal(t, current.Capella.GasLimit, payload.Capella.GasLimit) require.NotEqual(t, current.Capella.GasLimit, prev.Capella.GasLimit) @@ -304,17 +304,17 @@ func TestMemcached(t *testing.T) { submission, err := common.GetBlockSubmissionInfo(&tc.Input) require.NoError(t, err) - require.Equal(t, submission.Proposer.String(), pk.String()) + require.Equal(t, submission.BidTrace.ProposerPubkey.String(), pk.String()) - err = mem.SaveExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), payload) + err = mem.SaveExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), payload) require.NoError(t, err) - ret, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + ret, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String()) require.NoError(t, err) require.Equal(t, len(ret.Capella.Transactions), len(submission.Transactions)) time.Sleep((time.Duration(defaultMemcachedExpirySeconds) + 2) * time.Second) - expired, err := mem.GetExecutionPayload(submission.Slot, submission.Proposer.String(), submission.BlockHash.String()) + expired, err := mem.GetExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.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 34df16fe..f1f0e9b9 100644 --- a/datastore/redis.go +++ b/datastore/redis.go @@ -428,15 +428,15 @@ func (r *RedisCache) GetExecutionPayloadCapella(slot uint64, proposerPubkey, blo }, nil } -func (r *RedisCache) SaveBidTrace(ctx context.Context, pipeliner redis.Pipeliner, trace *common.BidTraceV2) (err error) { +func (r *RedisCache) SaveBidTrace(ctx context.Context, pipeliner redis.Pipeliner, trace *common.BidTraceV2WithBlobFields) (err error) { key := r.keyCacheBidTrace(trace.Slot, trace.ProposerPubkey.String(), trace.BlockHash.String()) return r.SetObjPipelined(ctx, pipeliner, key, trace, expiryBidCache) } // GetBidTrace returns (trace, nil), or (nil, redis.Nil) if the trace does not exist -func (r *RedisCache) GetBidTrace(slot uint64, proposerPubkey, blockHash string) (*common.BidTraceV2, error) { +func (r *RedisCache) GetBidTrace(slot uint64, proposerPubkey, blockHash string) (*common.BidTraceV2WithBlobFields, error) { key := r.keyCacheBidTrace(slot, proposerPubkey, blockHash) - resp := new(common.BidTraceV2) + resp := new(common.BidTraceV2WithBlobFields) err := r.GetObj(key, resp) return resp, err } @@ -502,7 +502,7 @@ type SaveBidAndUpdateTopBidResponse struct { TimeUpdateFloor time.Duration } -func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis.Pipeliner, trace *common.BidTraceV2, payload *common.VersionedSubmitBlockRequest, getPayloadResponse *builderApi.VersionedSubmitBlindedBlockResponse, getHeaderResponse *builderSpec.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.BidTraceV2WithBlobFields, payload *common.VersionedSubmitBlockRequest, getPayloadResponse *builderApi.VersionedSubmitBlindedBlockResponse, getHeaderResponse *builderSpec.VersionedSignedBuilderBid, reqReceivedAt time.Time, isCancellationEnabled bool, floorValue *big.Int) (state SaveBidAndUpdateTopBidResponse, err error) { var prevTime, nextTime time.Time prevTime = time.Now() @@ -512,14 +512,14 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis } // Load latest bids for a given slot+parent+proposer - builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) + builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) if err != nil { return state, err } // Load floor value (if not passed in already) if floorValue == nil { - floorValue, err = r.GetFloorBidValue(ctx, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) + floorValue, err = r.GetFloorBidValue(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) if err != nil { return state, err } @@ -533,7 +533,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 := submission.Value.ToBig().Cmp(floorValue) == 1 + isBidAboveFloor := submission.BidTrace.Value.ToBig().Cmp(floorValue) == 1 if !isCancellationEnabled && !isBidAboveFloor { return state, nil } @@ -549,12 +549,12 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis // 1. Save the execution payload switch payload.Version { case spec.DataVersionCapella: - err = r.SaveExecutionPayloadCapella(ctx, pipeliner, submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), getPayloadResponse.Capella) + err = r.SaveExecutionPayloadCapella(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), getPayloadResponse.Capella) if err != nil { return state, err } case spec.DataVersionDeneb: - err = r.SavePayloadContentsDeneb(ctx, pipeliner, submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), getPayloadResponse.Deneb) + err = r.SavePayloadContentsDeneb(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), getPayloadResponse.Deneb) if err != nil { return state, err } @@ -568,11 +568,11 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis prevTime = nextTime // 2. Save latest bid for this builder - err = r.SaveBuilderBid(ctx, pipeliner, submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), submission.Builder.String(), reqReceivedAt, getHeaderResponse) + err = r.SaveBuilderBid(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BuilderPubkey.String(), reqReceivedAt, getHeaderResponse) if err != nil { return state, err } - builderBids.bidValues[submission.Builder.String()] = submission.Value.ToBig() + builderBids.bidValues[submission.BidTrace.BuilderPubkey.String()] = submission.BidTrace.Value.ToBig() // Record time needed to save bid nextTime = time.Now().UTC() @@ -596,11 +596,11 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis return state, nil } - state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), floorValue) + state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), floorValue) if err != nil { return state, err } - state.IsNewTopBid = submission.Value.ToBig().Cmp(state.TopBidValue) == 0 + state.IsNewTopBid = submission.BidTrace.Value.ToBig().Cmp(state.TopBidValue) == 0 // An Exec happens in _updateTopBid. state.WasBidSaved = true @@ -614,8 +614,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis } // Non-cancellable bid above floor should set new floor - 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()) + keyBidSource := r.keyLatestBidByBuilder(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BuilderPubkey.String()) + keyFloorBid := r.keyFloorBid(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) c := pipeliner.Copy(ctx, keyBidSource, keyFloorBid, 0, true) _, err = pipeliner.Exec(ctx) if err != nil { @@ -633,8 +633,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis return state, err } - keyFloorBidValue := r.keyFloorBidValue(submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) - err = pipeliner.Set(ctx, keyFloorBidValue, submission.Value.Dec(), expiryBidCache).Err() + keyFloorBidValue := r.keyFloorBidValue(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) + err = pipeliner.Set(ctx, keyFloorBidValue, submission.BidTrace.Value.Dec(), expiryBidCache).Err() if err != nil { return state, err } diff --git a/datastore/redis_test.go b/datastore/redis_test.go index 2cc8a78b..2cf98f1b 100644 --- a/datastore/redis_test.go +++ b/datastore/redis_test.go @@ -130,7 +130,7 @@ func TestBuilderBids(t *testing.T) { Version: version, } - trace := &common.BidTraceV2{ + trace := &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ Value: uint256.NewInt(123), }, diff --git a/services/api/blocksim_ratelimiter.go b/services/api/blocksim_ratelimiter.go index 354a24a0..51296b54 100644 --- a/services/api/blocksim_ratelimiter.go +++ b/services/api/blocksim_ratelimiter.go @@ -87,7 +87,7 @@ func (b *BlockSimulationRateLimiter) Send(context context.Context, payload *comm // Prepare headers headers := http.Header{} - headers.Add("X-Request-ID", fmt.Sprintf("%d/%s", submission.Slot, submission.BlockHash.String())) + headers.Add("X-Request-ID", fmt.Sprintf("%d/%s", submission.BidTrace.Slot, submission.BidTrace.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 e7ba468c..b7a35b80 100644 --- a/services/api/optimistic_test.go +++ b/services/api/optimistic_test.go @@ -42,8 +42,8 @@ var ( errFake = fmt.Errorf("foo error") ) -func getTestBidTrace(pubkey phase0.BLSPubKey, value, slot uint64) *common.BidTraceV2 { - return &common.BidTraceV2{ +func getTestBidTrace(pubkey phase0.BLSPubKey, value, slot uint64) *common.BidTraceV2WithBlobFields { + return &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ Slot: slot, BuilderPubkey: pubkey, diff --git a/services/api/service.go b/services/api/service.go index e526ca7f..9dea594f 100644 --- a/services/api/service.go +++ b/services/api/service.go @@ -636,14 +636,14 @@ func (api *RelayAPI) processOptimisticBlock(opts blockSimOptions, simResultC cha opts.log.WithError(err).Error("error getting block submission info") return } - builderPubkey := submission.Builder.String() + builderPubkey := submission.BidTrace.BuilderPubkey.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", submission.BlockHash.String()) + }).Infof("simulating optimistic block with hash: %v", submission.BidTrace.BlockHash.String()) reqErr, simErr := api.simulateBlock(ctx, opts) simResultC <- &blockSimResult{reqErr == nil, true, reqErr, simErr} if reqErr != nil || simErr != nil { @@ -1593,18 +1593,18 @@ func (api *RelayAPI) handleBuilderGetValidators(w http.ResponseWriter, req *http } } -func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *logrus.Entry, submission *common.BlockSubmissionInfo) (uint64, bool) { +func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *logrus.Entry, bidTrace *builderApiV1.BidTrace) (uint64, bool) { api.proposerDutiesLock.RLock() - slotDuty := api.proposerDutiesMap[submission.Slot] + slotDuty := api.proposerDutiesMap[bidTrace.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(), submission.ProposerFeeRecipient.String()) { + } else if !strings.EqualFold(slotDuty.Entry.Message.FeeRecipient.String(), bidTrace.ProposerFeeRecipient.String()) { log.WithFields(logrus.Fields{ "expectedFeeRecipient": slotDuty.Entry.Message.FeeRecipient.String(), - "actualFeeRecipient": submission.ProposerFeeRecipient.String(), + "actualFeeRecipient": bidTrace.ProposerFeeRecipient.String(), }).Info("fee recipient does not match") api.RespondError(w, http.StatusBadRequest, "fee recipient does not match") return 0, false @@ -1614,11 +1614,11 @@ func (api *RelayAPI) checkSubmissionFeeRecipient(w http.ResponseWriter, log *log func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *logrus.Entry, submission *common.BlockSubmissionInfo) (payloadAttributesHelper, bool) { api.payloadAttributesLock.RLock() - attrs, ok := api.payloadAttributes[submission.ParentHash.String()] + attrs, ok := api.payloadAttributes[submission.BidTrace.ParentHash.String()] api.payloadAttributesLock.RUnlock() - if !ok || submission.Slot != attrs.slot { + if !ok || submission.BidTrace.Slot != attrs.slot { log.Info(ok) - log.Info("payload", submission.Slot, "attrs", attrs.slot) + log.Info("payload", submission.BidTrace.Slot, "attrs", attrs.slot) log.Warn("payload attributes not (yet) known") api.RespondError(w, http.StatusBadRequest, "payload attributes not (yet) known") return attrs, false @@ -1631,7 +1631,7 @@ func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *log return attrs, false } - if hasReachedFork(submission.Slot, api.capellaEpoch) { // Capella requires correct withdrawals + if hasReachedFork(submission.BidTrace.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") @@ -1651,26 +1651,26 @@ func (api *RelayAPI) checkSubmissionPayloadAttrs(w http.ResponseWriter, log *log } func (api *RelayAPI) checkSubmissionSlotDetails(w http.ResponseWriter, log *logrus.Entry, headSlot uint64, payload *common.VersionedSubmitBlockRequest, submission *common.BlockSubmissionInfo) bool { - if api.isDeneb(submission.Slot) && payload.Deneb == nil { + if api.isDeneb(submission.BidTrace.Slot) && payload.Deneb == nil { log.Info("rejecting submission - non deneb payload for deneb fork") api.RespondError(w, http.StatusBadRequest, "not deneb payload") return false } - if api.isCapella(submission.Slot) && payload.Capella == nil { + if api.isCapella(submission.BidTrace.Slot) && payload.Capella == nil { log.Info("rejecting submission - non capella payload for capella fork") api.RespondError(w, http.StatusBadRequest, "not capella payload") return false } - if submission.Slot <= headSlot { + if submission.BidTrace.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 + (submission.Slot * common.SecondsPerSlot) + expectedTimestamp := api.genesisInfo.Data.GenesisTime + (submission.BidTrace.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)) @@ -1726,14 +1726,14 @@ func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, bool) { slotLastPayloadDelivered, err := api.redis.GetLastSlotDelivered(context.Background(), opts.tx) if err != nil && !errors.Is(err, redis.Nil) { opts.log.WithError(err).Error("failed to get delivered payload slot from redis") - } else if opts.submission.Slot <= slotLastPayloadDelivered { + } else if opts.submission.BidTrace.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, false } // Grab floor bid value - floorBidValue, err := api.redis.GetFloorBidValue(context.Background(), opts.tx, opts.submission.Slot, opts.submission.ParentHash.String(), opts.submission.Proposer.String()) + floorBidValue, err := api.redis.GetFloorBidValue(context.Background(), opts.tx, opts.submission.BidTrace.Slot, opts.submission.BidTrace.ParentHash.String(), opts.submission.BidTrace.ProposerPubkey.String()) if err != nil { opts.log.WithError(err).Error("failed to get floor bid value from redis") } else { @@ -1743,12 +1743,12 @@ func (api *RelayAPI) checkFloorBidValue(opts bidFloorOpts) (*big.Int, bool) { // -------------------------------------------- // Skip submission if below the floor bid value // -------------------------------------------- - isBidBelowFloor := floorBidValue != nil && opts.submission.Value.ToBig().Cmp(floorBidValue) == -1 - isBidAtOrBelowFloor := floorBidValue != nil && opts.submission.Value.ToBig().Cmp(floorBidValue) < 1 + isBidBelowFloor := floorBidValue != nil && opts.submission.BidTrace.Value.ToBig().Cmp(floorBidValue) == -1 + isBidAtOrBelowFloor := floorBidValue != nil && opts.submission.BidTrace.Value.ToBig().Cmp(floorBidValue) < 1 if opts.cancellationsEnabled && isBidBelowFloor { // with cancellations: if below floor -> delete previous bid opts.simResultC <- &blockSimResult{false, false, nil, nil} opts.log.Info("submission below floor bid value, with cancellation") - err := api.redis.DelBuilderBid(context.Background(), opts.tx, opts.submission.Slot, opts.submission.ParentHash.String(), opts.submission.Proposer.String(), opts.submission.Builder.String()) + err := api.redis.DelBuilderBid(context.Background(), opts.tx, opts.submission.BidTrace.Slot, opts.submission.BidTrace.ParentHash.String(), opts.submission.BidTrace.ProposerPubkey.String(), opts.submission.BidTrace.BuilderPubkey.String()) 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") @@ -1798,10 +1798,13 @@ func (api *RelayAPI) updateRedisBid(opts redisUpdateBidOpts) (*datastore.SaveBid return nil, nil, false } - bidTrace := common.BidTraceV2{ - BidTrace: *submission.BidTrace, - BlockNumber: submission.BlockNumber, - NumTx: uint64(len(submission.Transactions)), + bidTrace := common.BidTraceV2WithBlobFields{ + BidTrace: *submission.BidTrace, + BlockNumber: submission.BlockNumber, + NumTx: uint64(len(submission.Transactions)), + NumBlobs: uint64(len(submission.Blobs)), + BlobGasUsed: submission.BlobGasUsed, + ExcessBlobGas: submission.ExcessBlobGas, } // @@ -1914,12 +1917,12 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque } log = log.WithFields(logrus.Fields{ "timestampAfterDecoding": time.Now().UTC().UnixMilli(), - "slot": submission.Slot, - "builderPubkey": submission.Builder.String(), - "blockHash": submission.BlockHash.String(), - "proposerPubkey": submission.Proposer.String(), - "parentHash": submission.ParentHash.String(), - "value": submission.Value.Dec(), + "slot": submission.BidTrace.Slot, + "builderPubkey": submission.BidTrace.BuilderPubkey.String(), + "blockHash": submission.BidTrace.BlockHash.String(), + "proposerPubkey": submission.BidTrace.ProposerPubkey.String(), + "parentHash": submission.BidTrace.ParentHash.String(), + "value": submission.BidTrace.Value.Dec(), "numTx": len(submission.Transactions), "payloadBytes": len(requestPayloadBytes), "isLargeRequest": isLargeRequest, @@ -1938,7 +1941,7 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque return } - builderPubkey := submission.Builder + builderPubkey := submission.BidTrace.BuilderPubkey builderEntry, ok := api.checkBuilderEntry(w, log, builderPubkey) if !ok { return @@ -1949,13 +1952,13 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque "timestampAfterChecks1": time.Now().UTC().UnixMilli(), }) - gasLimit, ok := api.checkSubmissionFeeRecipient(w, log, submission) + gasLimit, ok := api.checkSubmissionFeeRecipient(w, log, submission.BidTrace) if !ok { return } // Don't accept blocks with 0 value - if submission.Value.ToBig().Cmp(ZeroU256.BigInt()) == 0 || len(submission.Transactions) == 0 { + if submission.BidTrace.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 @@ -2047,11 +2050,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, submission.Slot, submission.ParentHash.String(), submission.Proposer.String()) + topBidValue, err := api.redis.GetTopBidValue(context.Background(), tx, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) if err != nil { log.WithError(err).Error("failed to get top bid value from redis") } else { - bidIsTopBid = submission.Value.ToBig().Cmp(topBidValue) == 1 + bidIsTopBid = submission.BidTrace.Value.ToBig().Cmp(topBidValue) == 1 log = log.WithFields(logrus.Fields{ "topBidValue": topBidValue.String(), "newBidIsTopBid": bidIsTopBid, @@ -2085,8 +2088,8 @@ func (api *RelayAPI) handleSubmitNewBlock(w http.ResponseWriter, req *http.Reque } // With sufficient collateral, process the block optimistically. if builderEntry.status.IsOptimistic && - builderEntry.collateral.Cmp(submission.Value.ToBig()) >= 0 && - submission.Slot == api.optimisticSlot.Load() { + builderEntry.collateral.Cmp(submission.BidTrace.Value.ToBig()) >= 0 && + submission.BidTrace.Slot == api.optimisticSlot.Load() { go api.processOptimisticBlock(opts, simResultC) } else { // Simulate block (synchronously). @@ -2127,7 +2130,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, submission.Slot, submission.Builder.String(), submission.ParentHash.String(), submission.Proposer.String()) + latestPayloadReceivedAt, err := api.redis.GetBuilderLatestPayloadReceivedAt(context.Background(), tx, submission.BidTrace.Slot, submission.BidTrace.BuilderPubkey.String(), submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String()) if err != nil { log.WithError(err).Error("failed getting latest payload receivedAt from redis") } else if receivedAt.UnixMilli() < latestPayloadReceivedAt { @@ -2171,7 +2174,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(submission.Slot, submission.Proposer.String(), submission.BlockHash.String(), getPayloadResponse) + err = api.memcached.SaveExecutionPayload(submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.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 4170b513..6e490075 100644 --- a/services/api/service_test.go +++ b/services/api/service_test.go @@ -218,7 +218,7 @@ func TestGetHeader(t *testing.T) { proposerPubkey := "0x6ae5932d1e248d987d51b58665b81848814202d7b23b343d20f2a167d12f07dcb01ca41c42fdd60b7fca9c4b90890792" builderPubkey := "0xfa1ed37c3553d0ce1e9349b2c5063cf6e394d231c8d3e0df75e9462257c081543086109ffddaacc0aa76f33dc9661c83" bidValue := uint256.NewInt(99) - trace := &common.BidTraceV2{ + trace := &common.BidTraceV2WithBlobFields{ BidTrace: builderApiV1.BidTrace{ Value: bidValue, }, @@ -620,7 +620,7 @@ func TestCheckSubmissionFeeRecipient(t *testing.T) { log := logrus.NewEntry(logger) submission, err := common.GetBlockSubmissionInfo(tc.payload) require.NoError(t, err) - gasLimit, ok := backend.relay.checkSubmissionFeeRecipient(w, log, submission) + gasLimit, ok := backend.relay.checkSubmissionFeeRecipient(w, log, submission.BidTrace) require.Equal(t, tc.expectGasLimit, gasLimit) require.Equal(t, tc.expectOk, ok) }) @@ -838,6 +838,7 @@ func TestCheckSubmissionSlotDetails(t *testing.T) { ExecutionPayload: &deneb.ExecutionPayload{ Timestamp: testSlot * common.SecondsPerSlot, }, + BlobsBundle: &builderApiDeneb.BlobsBundle{}, Message: &builderApiV1.BidTrace{ Slot: testSlot, }, @@ -1037,7 +1038,7 @@ func TestCheckFloorBidValue(t *testing.T) { _, _, backend := startTestBackend(t) submission, err := common.GetBlockSubmissionInfo(tc.payload) require.NoError(t, err) - err = backend.redis.SetFloorBidValue(submission.Slot, submission.ParentHash.String(), submission.Proposer.String(), tc.floorValue) + err = backend.redis.SetFloorBidValue(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), tc.floorValue) require.Nil(t, err) w := httptest.NewRecorder() diff --git a/services/api/utils.go b/services/api/utils.go index e0fde907..b0ad0281 100644 --- a/services/api/utils.go +++ b/services/api/utils.go @@ -30,11 +30,11 @@ func SanityCheckBuilderBlockSubmission(payload *common.VersionedSubmitBlockReque if err != nil { return err } - if submission.BlockHash.String() != submission.ExecutionPayloadBlockHash.String() { + if submission.BidTrace.BlockHash.String() != submission.ExecutionPayloadBlockHash.String() { return ErrBlockHashMismatch } - if submission.ParentHash.String() != submission.ExecutionPayloadParentHash.String() { + if submission.BidTrace.ParentHash.String() != submission.ExecutionPayloadParentHash.String() { return ErrParentHashMismatch } diff --git a/services/website/website.html b/services/website/website.html index 877a6b76..977af627 100644 --- a/services/website/website.html +++ b/services/website/website.html @@ -122,6 +122,11 @@ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='263.27 168.54 81.35 101.17'%3E%3Cpath d='M 341.287 198.092 L 340.508 198.289 C 338.293 198.85 337.797 198.496 336.846 196.409 C 335.8 193.738 334.475 191.177 332.89 188.769 C 325.505 178.925 315.692 174.652 303.269 176.552 C 301.781 176.788 300.881 176.336 300.517 174.869 C 300.294 173.884 300.041 172.998 299.829 172.053 C 299.445 170.36 300.001 169.513 301.711 169.208 C 312.434 167.239 322.146 169.631 330.887 175.962 C 337.423 180.697 341.702 187.076 344.363 194.666 C 344.98 196.33 344.484 197.216 342.704 197.708 L 342.704 197.738 Z M 329.865 201.006 C 328.166 201.439 327.842 199.894 326.578 196.94 C 323.522 189.645 315.884 185.189 307.831 186.003 C 305.252 186.259 303.461 186.712 303.117 185.343 C 302.055 181.041 302.237 180.795 306.688 180.421 C 317.858 179.693 328.237 186.055 332.385 196.172 C 332.476 196.419 332.567 196.665 332.678 196.911 C 333.73 199.342 333.366 200.081 330.796 200.77 Z M 323.866 199.923 C 323.927 200.071 323.988 200.228 324.058 200.376 C 324.726 201.882 324.483 202.345 322.824 202.778 L 322.227 202.926 C 321.135 203.201 320.963 202.237 320.133 200.396 C 318.102 195.813 313.219 193.07 308.125 193.652 C 306.455 193.829 305.302 194.125 305.09 193.268 C 304.422 190.581 304.543 190.423 307.406 190.167 C 314.489 189.592 321.142 193.536 323.866 199.923 Z M 274.143 222.713 C 271.866 216.439 271.442 209.669 272.919 203.172 C 273.778 199.234 275.144 198.673 278.473 200.839 C 285.554 205.446 292.515 209.935 300.082 214.838 C 300.82 213.528 301.802 212.278 302.459 210.851 C 302.775 210.139 302.883 209.355 302.773 208.586 C 302.267 205.525 303.785 202.827 306.648 202.138 C 309.515 201.493 312.366 203.272 312.96 206.076 C 313.579 208.883 311.831 211.669 308.985 212.416 C 308.512 212.509 308.076 212.73 307.73 213.056 C 307.558 213.312 304.624 217.899 304.624 217.899 L 315.014 224.889 C 318.525 227.242 322.005 229.575 325.546 231.879 C 329.086 234.182 328.975 235.639 325.738 238.179 C 308.954 251.233 282.104 244.972 274.143 222.713 Z M 263.271 269.704 L 263.271 269.655 C 263.758 268.742 276.643 244.586 277.269 243.948 C 278.645 242.55 298.322 252.808 299.525 254.019 C 299.778 254.275 304.988 269.682 304.988 269.682 L 304.988 269.704 Z'%3E%3C/path%3E%3C/svg%3E"); } + .img-etherscan { + background-repeat: no-repeat; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 123 123' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M25.79 58.4149C25.7901 57.7357 25.9244 57.0633 26.1851 56.4361C26.4458 55.809 26.8278 55.2396 27.3092 54.7605C27.7907 54.2814 28.3619 53.9021 28.9903 53.6444C29.6187 53.3867 30.2918 53.2557 30.971 53.2589L39.561 53.2869C40.9305 53.2869 42.244 53.831 43.2124 54.7994C44.1809 55.7678 44.725 57.0813 44.725 58.4509V90.9309C45.692 90.6439 46.934 90.3379 48.293 90.0179C49.237 89.7962 50.0783 89.262 50.6805 88.5019C51.2826 87.7418 51.6102 86.8006 51.61 85.8309V45.5409C51.61 44.1712 52.154 42.8576 53.1224 41.889C54.0908 40.9204 55.4043 40.3762 56.774 40.3759H65.381C66.7506 40.3762 68.0641 40.9204 69.0325 41.889C70.0009 42.8576 70.545 44.1712 70.545 45.5409V82.9339C70.545 82.9339 72.7 82.0619 74.799 81.1759C75.5787 80.8462 76.2441 80.2941 76.7122 79.5886C77.1803 78.8832 77.4302 78.0555 77.431 77.2089V32.6309C77.431 31.2615 77.9749 29.9481 78.9431 28.9797C79.9113 28.0113 81.2245 27.4672 82.5939 27.4669H91.201C92.5706 27.4669 93.884 28.0109 94.8525 28.9794C95.8209 29.9478 96.365 31.2613 96.365 32.6309V69.3399C103.827 63.9319 111.389 57.4279 117.39 49.6069C118.261 48.4717 118.837 47.1386 119.067 45.7267C119.297 44.3148 119.174 42.8678 118.709 41.5149C115.931 33.5227 111.516 26.1983 105.745 20.0105C99.974 13.8228 92.9749 8.90785 85.1955 5.58032C77.4161 2.2528 69.0277 0.585938 60.5671 0.686416C52.1065 0.786893 43.7601 2.6525 36.062 6.16383C28.3638 9.67517 21.4834 14.7549 15.8611 21.078C10.2388 27.401 5.99842 34.8282 3.41131 42.8842C0.824207 50.9401 -0.0526487 59.4474 0.836851 67.8617C1.72635 76.276 4.36263 84.4119 8.57696 91.7489C9.31111 93.0145 10.3912 94.0444 11.6903 94.7175C12.9894 95.3906 14.4536 95.679 15.911 95.5489C17.539 95.4059 19.566 95.2029 21.976 94.9199C23.0251 94.8008 23.9937 94.2999 24.6972 93.5126C25.4008 92.7253 25.7901 91.7067 25.791 90.6509L25.79 58.4149Z' fill='%2321325B'/%3E%3Cpath d='M25.6021 110.51C34.6744 117.11 45.3959 121.072 56.5802 121.957C67.7646 122.841 78.9757 120.615 88.9731 115.523C98.9705 110.431 107.364 102.673 113.226 93.1068C119.087 83.5405 122.188 72.539 122.185 61.3197C122.185 59.9197 122.12 58.5347 122.027 57.1577C99.808 90.2957 58.7831 105.788 25.604 110.505' fill='%23979695'/%3E%3C/svg%3E"); + } + @media (max-width: 768px) { .links { display: none; @@ -240,7 +245,8 @@

- Num tx + Num txs + Blobs Block hash @@ -258,6 +264,9 @@

{{.BlockNumber | prettyInt}} {{.Value | weiToEth}} {{.NumTx }} + +
{{.NumBlobs }}
+ {{.BlockHash}}
@@ -266,7 +275,7 @@

  {{ end }} {{ if ne $linkEtherscan "" }} - View block on Etherscan + {{ end }}

diff --git a/testdata/website-htmldata.json b/testdata/website-htmldata.json index 6fab7864..5593dbe1 100644 --- a/testdata/website-htmldata.json +++ b/testdata/website-htmldata.json @@ -40,6 +40,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -64,6 +66,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -88,6 +92,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -112,6 +118,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -136,6 +144,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -160,6 +170,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -184,6 +196,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -208,6 +222,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -232,6 +248,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -256,6 +274,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -280,6 +300,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -304,6 +326,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -328,6 +352,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -352,6 +378,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -376,6 +404,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -400,6 +430,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -424,6 +456,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -448,6 +482,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -472,6 +508,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -496,6 +534,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -520,6 +560,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -544,6 +586,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -568,6 +612,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -592,6 +638,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -616,6 +664,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -640,6 +690,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -664,6 +716,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -688,6 +742,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -712,6 +768,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" }, { @@ -736,6 +794,8 @@ "GasUsed": 21000, "GasLimit": 30000000, "NumTx": 1, + "NumBlobs": 1, + "BlobGasUsed": 21000, "Value": "21000" } ]