Skip to content

Commit 44a89d3

Browse files
committed
Add optimistic header v2 endpoint
1 parent 4e02e4b commit 44a89d3

File tree

10 files changed

+865
-170
lines changed

10 files changed

+865
-170
lines changed

common/common.go

+21-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ var (
1717

1818
SlotsPerEpoch = uint64(cli.GetEnvInt("SLOTS_PER_EPOCH", 32))
1919
DurationPerEpoch = DurationPerSlot * time.Duration(SlotsPerEpoch)
20+
21+
EmptyTxRoot = "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1"
2022
)
2123

2224
func SlotToEpoch(slot uint64) uint64 {
@@ -38,10 +40,10 @@ type BuilderStatus struct {
3840
IsOptimistic bool
3941
}
4042

41-
// Profile captures performance metrics for the block submission handler. Each
43+
// BlockSubmissionProfile captures performance metrics for the block submission handler. Each
4244
// field corresponds to the number of microseconds in each stage. The `Total`
4345
// field is the number of microseconds taken for entire flow.
44-
type Profile struct {
46+
type BlockSubmissionProfile struct {
4547
PayloadLoad uint64
4648
Decode uint64
4749
Prechecks uint64
@@ -50,6 +52,22 @@ type Profile struct {
5052
Total uint64
5153
}
5254

53-
func (p *Profile) String() string {
55+
func (p *BlockSubmissionProfile) String() string {
5456
return fmt.Sprintf("%v,%v,%v,%v,%v", p.Decode, p.Prechecks, p.Simulation, p.RedisUpdate, p.Total)
5557
}
58+
59+
// HeaderSubmissionProfile captures performance metrics for the header submission handler. Each
60+
// field corresponds to the number of microseconds at the start of each stage.
61+
type HeaderSubmissionProfile struct {
62+
PayloadLoad uint64
63+
Decode uint64
64+
Prechecks uint64
65+
Signature uint64
66+
RedisChecks uint64
67+
RedisUpdate uint64
68+
Total uint64
69+
}
70+
71+
func (p *HeaderSubmissionProfile) String() string {
72+
return fmt.Sprintf("%v,%v,%v,%v,%v,%v,%v", p.PayloadLoad, p.Decode, p.Prechecks, p.Signature, p.RedisChecks, p.RedisUpdate, p.Total)
73+
}

common/test_utils.go

+5
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,14 @@ func CreateTestBlockSubmission(t *testing.T, builderPubkey string, value *uint25
186186
Message: bidTrace,
187187
ExecutionPayload: &deneb.ExecutionPayload{ //nolint:exhaustruct
188188
BaseFeePerGas: uint256.NewInt(0),
189+
ExtraData: make([]byte, 32),
190+
Transactions: make([]bellatrix.Transaction, 0),
191+
Withdrawals: make([]*capella.Withdrawal, 0),
189192
},
190193
BlobsBundle: &builderApiDeneb.BlobsBundle{ //nolint:exhaustruct
191194
Commitments: make([]deneb.KZGCommitment, 0),
195+
Proofs: make([]deneb.KZGProof, 0),
196+
Blobs: make([]deneb.Blob, 0),
192197
},
193198
Signature: phase0.BLSSignature{},
194199
},

database/database.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type IDatabaseService interface {
2525
GetValidatorRegistration(pubkey string) (*ValidatorRegistrationEntry, error)
2626
GetValidatorRegistrationsForPubkeys(pubkeys []string) ([]*ValidatorRegistrationEntry, error)
2727

28-
SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error)
28+
SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.BlockSubmissionProfile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error)
2929
GetBlockSubmissionEntry(slot uint64, proposerPubkey, blockHash string) (entry *BuilderBlockSubmissionEntry, err error)
3030
GetBuilderSubmissions(filters GetBuilderSubmissionsFilters) ([]*BuilderBlockSubmissionEntry, error)
3131
GetBuilderSubmissionsBySlots(slotFrom, slotTo uint64) (entries []*BuilderBlockSubmissionEntry, err error)
@@ -175,7 +175,7 @@ func (s *DatabaseService) GetLatestValidatorRegistrations(timestampOnly bool) ([
175175
return registrations, err
176176
}
177177

178-
func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) {
178+
func (s *DatabaseService) SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.BlockSubmissionProfile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) {
179179
// 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)
180180
execPayloadEntry, err := PayloadToExecPayloadEntry(payload)
181181
if err != nil {

database/database_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ var (
3939
feeRecipient = bellatrix.ExecutionAddress{0x02}
4040
blockHashStr = "0xa645370cc112c2e8e3cce121416c7dc849e773506d4b6fb9b752ada711355369"
4141
testDBDSN = common.GetEnv("TEST_DB_DSN", "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable")
42-
profile = common.Profile{
42+
profile = common.BlockSubmissionProfile{
4343
Decode: 42,
4444
Prechecks: 43,
4545
Simulation: 44,

database/mockdb.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (db MockDB) GetLatestValidatorRegistrations(timestampOnly bool) ([]*Validat
3636
return nil, nil
3737
}
3838

39-
func (db MockDB) SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.Profile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) {
39+
func (db MockDB) SaveBuilderBlockSubmission(payload *common.VersionedSubmitBlockRequest, requestError, validationError error, receivedAt, eligibleAt time.Time, wasSimulated, saveExecPayload bool, profile common.BlockSubmissionProfile, optimisticSubmission bool) (entry *BuilderBlockSubmissionEntry, err error) {
4040
return nil, nil
4141
}
4242

datastore/redis.go

+56-48
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
builderApi "github.com/attestantio/go-builder-client/api"
1414
builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb"
15+
builderApiV1 "github.com/attestantio/go-builder-client/api/v1"
1516
builderSpec "github.com/attestantio/go-builder-client/spec"
1617
"github.com/attestantio/go-eth2-client/spec"
1718
"github.com/attestantio/go-eth2-client/spec/capella"
@@ -496,31 +497,26 @@ type SaveBidAndUpdateTopBidResponse struct {
496497
PrevTopBidValue *big.Int
497498

498499
TimePrep time.Duration
499-
TimeSavePayload time.Duration
500500
TimeSaveBid time.Duration
501501
TimeSaveTrace time.Duration
502+
TimeSavePayload time.Duration
502503
TimeUpdateTopBid time.Duration
503504
TimeUpdateFloor time.Duration
504505
}
505506

506-
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) {
507+
func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis.Pipeliner, trace *builderApiV1.BidTrace, blockSubmission *common.BlockSubmissionInfo, getPayloadResponse *builderApi.VersionedSubmitBlindedBlockResponse, getHeaderResponse *builderSpec.VersionedSignedBuilderBid, reqReceivedAt time.Time, isCancellationEnabled bool, floorValue *big.Int) (state SaveBidAndUpdateTopBidResponse, err error) {
507508
var prevTime, nextTime time.Time
508509
prevTime = time.Now()
509510

510-
submission, err := common.GetBlockSubmissionInfo(payload)
511-
if err != nil {
512-
return state, err
513-
}
514-
515511
// Load latest bids for a given slot+parent+proposer
516-
builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String())
512+
builderBids, err := NewBuilderBidsFromRedis(ctx, r, pipeliner, trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String())
517513
if err != nil {
518514
return state, err
519515
}
520516

521517
// Load floor value (if not passed in already)
522518
if floorValue == nil {
523-
floorValue, err = r.GetFloorBidValue(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String())
519+
floorValue, err = r.GetFloorBidValue(ctx, pipeliner, trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String())
524520
if err != nil {
525521
return state, err
526522
}
@@ -534,7 +530,7 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis
534530
state.PrevTopBidValue = state.TopBidValue
535531

536532
// Abort now if non-cancellation bid is lower than floor value
537-
isBidAboveFloor := submission.BidTrace.Value.ToBig().Cmp(floorValue) == 1
533+
isBidAboveFloor := trace.Value.ToBig().Cmp(floorValue) == 1
538534
if !isCancellationEnabled && !isBidAboveFloor {
539535
return state, nil
540536
}
@@ -547,61 +543,73 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis
547543
//
548544
// Time to save things in Redis
549545
//
550-
// 1. Save the execution payload
551-
switch payload.Version {
552-
case spec.DataVersionCapella:
553-
err = r.SaveExecutionPayloadCapella(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), getPayloadResponse.Capella)
554-
if err != nil {
555-
return state, err
556-
}
557-
case spec.DataVersionDeneb:
558-
err = r.SavePayloadContentsDeneb(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BlockHash.String(), getPayloadResponse.Deneb)
559-
if err != nil {
560-
return state, err
561-
}
562-
case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix:
563-
return state, fmt.Errorf("unsupported payload version: %s", payload.Version) //nolint:goerr113
564-
}
565-
566-
// Record time needed to save payload
567-
nextTime = time.Now().UTC()
568-
state.TimeSavePayload = nextTime.Sub(prevTime)
569-
prevTime = nextTime
570-
571-
// 2. Save latest bid for this builder
572-
err = r.SaveBuilderBid(ctx, pipeliner, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BuilderPubkey.String(), reqReceivedAt, getHeaderResponse)
546+
// 1. Save latest bid for this builder
547+
err = r.SaveBuilderBid(ctx, pipeliner, trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String(), trace.BuilderPubkey.String(), reqReceivedAt, getHeaderResponse)
573548
if err != nil {
574549
return state, err
575550
}
576-
builderBids.bidValues[submission.BidTrace.BuilderPubkey.String()] = submission.BidTrace.Value.ToBig()
551+
builderBids.bidValues[trace.BuilderPubkey.String()] = trace.Value.ToBig()
577552

578553
// Record time needed to save bid
579554
nextTime = time.Now().UTC()
580555
state.TimeSaveBid = nextTime.Sub(prevTime)
581556
prevTime = nextTime
582557

583-
// 3. Save the bid trace
584-
err = r.SaveBidTrace(ctx, pipeliner, trace)
585-
if err != nil {
586-
return state, err
558+
// 2. Save the bid trace
559+
if blockSubmission != nil {
560+
bidTrace := common.BidTraceV2WithBlobFields{
561+
BidTrace: *trace,
562+
BlockNumber: blockSubmission.BlockNumber,
563+
NumTx: uint64(len(blockSubmission.Transactions)),
564+
NumBlobs: uint64(len(blockSubmission.Blobs)),
565+
BlobGasUsed: blockSubmission.BlobGasUsed,
566+
ExcessBlobGas: blockSubmission.ExcessBlobGas,
567+
}
568+
err = r.SaveBidTrace(ctx, pipeliner, &bidTrace)
569+
if err != nil {
570+
return state, err
571+
}
572+
573+
// Record time needed to save trace
574+
nextTime = time.Now().UTC()
575+
state.TimeSaveTrace = nextTime.Sub(prevTime)
576+
prevTime = nextTime
587577
}
588578

589-
// Record time needed to save trace
590-
nextTime = time.Now().UTC()
591-
state.TimeSaveTrace = nextTime.Sub(prevTime)
592-
prevTime = nextTime
579+
// 3. Save the execution payload
580+
if getPayloadResponse != nil {
581+
switch getPayloadResponse.Version {
582+
case spec.DataVersionCapella:
583+
err = r.SaveExecutionPayloadCapella(ctx, pipeliner, trace.Slot, trace.ProposerPubkey.String(), trace.BlockHash.String(), getPayloadResponse.Capella)
584+
if err != nil {
585+
return state, err
586+
}
587+
case spec.DataVersionDeneb:
588+
err = r.SavePayloadContentsDeneb(ctx, pipeliner, trace.Slot, trace.ProposerPubkey.String(), trace.BlockHash.String(), getPayloadResponse.Deneb)
589+
if err != nil {
590+
return state, err
591+
}
592+
case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix:
593+
return state, fmt.Errorf("unsupported payload version: %s", getPayloadResponse.Version) //nolint:goerr113
594+
}
595+
596+
// Record time needed to save payload
597+
nextTime = time.Now().UTC()
598+
state.TimeSavePayload = nextTime.Sub(prevTime)
599+
prevTime = nextTime
600+
}
593601

594602
// If top bid value hasn't change, abort now
595603
_, state.TopBidValue = builderBids.getTopBid()
596604
if state.TopBidValue.Cmp(state.PrevTopBidValue) == 0 {
597605
return state, nil
598606
}
599607

600-
state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), floorValue)
608+
state, err = r._updateTopBid(ctx, pipeliner, state, builderBids, trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String(), floorValue)
601609
if err != nil {
602610
return state, err
603611
}
604-
state.IsNewTopBid = submission.BidTrace.Value.ToBig().Cmp(state.TopBidValue) == 0
612+
state.IsNewTopBid = trace.Value.ToBig().Cmp(state.TopBidValue) == 0
605613
// An Exec happens in _updateTopBid.
606614
state.WasBidSaved = true
607615

@@ -615,8 +623,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis
615623
}
616624

617625
// Non-cancellable bid above floor should set new floor
618-
keyBidSource := r.keyLatestBidByBuilder(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String(), submission.BidTrace.BuilderPubkey.String())
619-
keyFloorBid := r.keyFloorBid(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String())
626+
keyBidSource := r.keyLatestBidByBuilder(trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String(), trace.BuilderPubkey.String())
627+
keyFloorBid := r.keyFloorBid(trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String())
620628
c := pipeliner.Copy(ctx, keyBidSource, keyFloorBid, 0, true)
621629
_, err = pipeliner.Exec(ctx)
622630
if err != nil {
@@ -634,8 +642,8 @@ func (r *RedisCache) SaveBidAndUpdateTopBid(ctx context.Context, pipeliner redis
634642
return state, err
635643
}
636644

637-
keyFloorBidValue := r.keyFloorBidValue(submission.BidTrace.Slot, submission.BidTrace.ParentHash.String(), submission.BidTrace.ProposerPubkey.String())
638-
err = pipeliner.Set(ctx, keyFloorBidValue, submission.BidTrace.Value.Dec(), expiryBidCache).Err()
645+
keyFloorBidValue := r.keyFloorBidValue(trace.Slot, trace.ParentHash.String(), trace.ProposerPubkey.String())
646+
err = pipeliner.Set(ctx, keyFloorBidValue, trace.Value.Dec(), expiryBidCache).Err()
639647
if err != nil {
640648
return state, err
641649
}

0 commit comments

Comments
 (0)