payload_bidder

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: GPL-3.0 Imports: 27 Imported by: 0

Documentation

Overview

Package payload_bidder holds the shared, transport-independent mechanics for post-Gloas builder participation: constructing and signing execution payload bids and the corresponding execution payload envelope (reveal). Both the ePBS p2p submitter and the HTTP Builder API build on it; each supplies its own economics (bid value, execution payment) while the construction, hash-tree- root (always via dynamic-ssz so preset-dependent sizes resolve), signing domain, and fork handling live here.

Everything is fork-agnostic: bids and envelopes use the go-eth2-client spec/all union types and read the active fork from the payload, so adding a future fork is confined to the spec/all view tables.

Index

Constants

View Source
const (
	// RevealSkipReasonPlanDisabled marks a reveal suppressed by the slot's
	// frozen action plan.
	RevealSkipReasonPlanDisabled = "plan_disabled"
	// RevealSkipReasonDisabled marks a reveal suppressed by the global
	// reveal.enabled switch.
	RevealSkipReasonDisabled = "disabled"
	// RevealSkipReasonLate marks a reveal request that arrived after the
	// slot's deadline (only applies without a deadline bypass).
	RevealSkipReasonLate = "late"
	// RevealSkipReasonVoteGateTimeout marks a vote-gated reveal whose
	// participation threshold was never reached before the slot expired.
	RevealSkipReasonVoteGateTimeout = "vote_gate_timeout"
)

Skip reasons carried by RevealResult.SkipReason on skipped reveals.

View Source
const (
	WonBlockSourceBuilderAPI = "builder_api"
	WonBlockSourceEPBS       = "epbs"
)

WonBlockSource identifies which subsystem delivered a won block.

View Source
const ProposerPreferencesNamespace = "proposer_preferences"

ProposerPreferencesNamespace is the kv_store namespace holding the cached proposer preferences.

View Source
const WonBlocksNamespace = "won_blocks"

WonBlocksNamespace is the kv_store namespace that held the persisted won-block records. Retained (with WonBlock/WonBlockCodec) so the slot results store can migrate the namespace; kept for at least one release.

Variables

View Source
var DomainBeaconBuilder = phase0.DomainType{0x0B, 0x00, 0x00, 0x00}

DomainBeaconBuilder is DOMAIN_BEACON_BUILDER — the signing domain for execution payload bids and execution payload envelopes.

Functions

func BuildSignedBid

func BuildSignedBid(
	ctx context.Context,
	p *payload_builder.Payload,
	params BidParams,
	s *Signer,
	forkVersion phase0.Version,
	genesisValidatorsRoot phase0.Root,
) (*eth2all.SignedExecutionPayloadBid, error)

BuildSignedBid constructs and signs a fork-agnostic SignedExecutionPayloadBid for the given payload. The fork is read from the payload; all payload-derived fields (parent hashes, block hash, randao, gas limit, commitments, execution requests root, slot) are filled here, and only the transport's economics + identity come in via params.

func BuildSignedEnvelope

func BuildSignedEnvelope(
	ctx context.Context,
	p *payload_builder.Payload,
	rc RevealContext,
	s *Signer,
	forkVersion phase0.Version,
	genesisValidatorsRoot phase0.Root,
) (signed *eth2all.SignedExecutionPayloadEnvelope, blobs, proofs [][]byte, err error)

BuildSignedEnvelope constructs and signs a fork-agnostic SignedExecutionPayloadEnvelope for the given payload, and returns the blobs and KZG proofs to publish alongside it. The fork-agnostic envelope embeds the canonical payload directly (no per-fork conversion needed).

Types

type BidParams

type BidParams struct {
	BuilderIndex     uint64
	FeeRecipient     bellatrix.ExecutionAddress
	Value            phase0.Gwei
	ExecutionPayment phase0.Gwei

	// Transform, when set, is an operator-supplied jq expression applied to the
	// bid MESSAGE (JSON) before signing; the modified message is then signed,
	// so the resulting bid is validly signed but customized.
	Transform string
}

BidParams are the policy-decided inputs a transport supplies for a bid. The transport owns the economics (value, execution payment) and identity (builder index, fee recipient); every other bid field is derived from the payload.

type InclusionTracker added in v0.0.2

type InclusionTracker struct {
	// contains filtered or unexported fields
}

InclusionTracker watches head events and detects inclusion of our payloads. It records the payment obligation, requests the payload reveal, fires inclusion events carrying the won-block summary (won-block storage is owned by the slot results tracker), and checks the follow-up block to detect orphaned (unrevealed) payloads. Shared by both the p2p and Builder API flows.

func NewInclusionTracker added in v0.0.2

func NewInclusionTracker(
	clClient *beacon.Client,
	chainSvc chain.Service,
	builderSvc *payload_builder.Service,
	registry *builder_keys.Registry,
	revealSvc *RevealService,
	payments *PaymentTracker,
	log logrus.FieldLogger,
) *InclusionTracker

NewInclusionTracker creates a new inclusion tracker. revealSvc and payments may be nil (pre-Gloas networks); the tracker then only fires inclusion events.

func (*InclusionTracker) Start added in v0.0.2

func (t *InclusionTracker) Start(ctx context.Context) error

Start starts the inclusion tracker's main loop.

func (*InclusionTracker) Stop added in v0.0.2

func (t *InclusionTracker) Stop()

Stop stops the inclusion tracker and waits for the main loop to exit.

func (*InclusionTracker) SubscribeIncluded added in v0.0.2

func (t *InclusionTracker) SubscribeIncluded(capacity int, blocking bool) *utils.Subscription[*PayloadIncludedEvent]

SubscribeIncluded subscribes to payload inclusion events.

func (*InclusionTracker) SubscribePayloadStatus added in v0.0.3

func (t *InclusionTracker) SubscribePayloadStatus(capacity int, blocking bool) *utils.Subscription[*PayloadStatusEvent]

SubscribePayloadStatus subscribes to the canonical/missed verdicts derived from the follow-up block after a won slot (Gloas+ only).

type PayloadIncludedEvent added in v0.0.2

type PayloadIncludedEvent struct {
	Payload      *payload_builder.Payload
	BlockInfo    *beacon.BlockInfo
	BidValueGwei uint64
	WonBlock     *WonBlock // the won-block summary for this inclusion
}

PayloadIncludedEvent is fired when a beacon block committing to one of our payloads is observed at the head (consumed by the WebUI and the slot results tracker).

type PayloadStatusEvent added in v0.0.3

type PayloadStatusEvent struct {
	Slot           phase0.Slot // the won slot
	Verdict        PayloadVerdict
	NextBlockSlot  phase0.Slot   // canonical block the verdict was derived from
	NextParentHash phase0.Hash32 // execution parent hash that block committed to
}

PayloadStatusEvent reports the (possibly revised) canonical verdict for a won slot's payload.

type PayloadVerdict added in v0.0.3

type PayloadVerdict string

PayloadVerdict is the canonical outcome of a won slot's payload, derived from the current head's ancestry (Gloas+ only). Verdicts are re-evaluated on every head event within the tracking window, so a reorg can flip them; each change fires a new PayloadStatusEvent.

const (
	// PayloadVerdictCanonical: the canonical chain's first block after the
	// won slot builds on our payload.
	PayloadVerdictCanonical PayloadVerdict = "canonical"
	// PayloadVerdictMissed: the won block is canonical but the next block
	// builds on an older execution block — the payload was withheld, revealed
	// too late, or voted empty.
	PayloadVerdictMissed PayloadVerdict = "missed"
	// PayloadVerdictOrphaned: the won beacon block itself was reorged out of
	// the canonical chain.
	PayloadVerdictOrphaned PayloadVerdict = "orphaned"
)

type PaymentTracker added in v0.0.2

type PaymentTracker struct {
	// contains filtered or unexported fields
}

PaymentTracker tracks payment obligations and live balance adjustments per builder key, across both bid flows (p2p and Builder API). Fed by the InclusionTracker (won bids) and RevealService (reveals); consumed by the lifecycle manager (top-ups), the key registry (effective balances) and the WebUI. Passive and thread-safe: it runs no goroutine of its own.

Accounting is per key because a payment is owed by the key whose bid won — with several managed keys bidding, charging the wrong one would let an underfunded key keep bidding while a funded one looks broke.

func NewPaymentTracker added in v0.0.2

func NewPaymentTracker(chainSvc chain.Service, log logrus.FieldLogger) *PaymentTracker

NewPaymentTracker creates a new payment tracker.

func (*PaymentTracker) AddDeposit added in v0.0.2

func (t *PaymentTracker) AddDeposit(keyIndex, amount uint64)

AddDeposit credits a deposit/topup to the key's live balance adjustment, anchored to the current epoch. The credit is reconciled away by ReconcileToEpoch once the authoritative snapshot advances past that epoch.

func (*PaymentTracker) GetBalanceAdjustment added in v0.0.2

func (t *PaymentTracker) GetBalanceAdjustment(keyIndex uint64) int64

GetBalanceAdjustment returns the key's cumulative balance adjustment since the last state refresh. It implements builder_keys.BalanceAdjuster.

func (*PaymentTracker) GetPendingPayments added in v0.0.3

func (t *PaymentTracker) GetPendingPayments(keyIndex uint64) uint64

GetPendingPayments returns one key's unrevealed won bid obligations.

func (*PaymentTracker) GetTotalPendingPayments added in v0.0.2

func (t *PaymentTracker) GetTotalPendingPayments() uint64

GetTotalPendingPayments returns the sum of unrevealed won bid obligations across all keys (disputed payments — winning block reorged out — excluded).

func (*PaymentTracker) MarkRevealed added in v0.0.2

func (t *PaymentTracker) MarkRevealed(keyIndex uint64, slot phase0.Slot)

MarkRevealed moves a won bid from pending to an immediate balance deduction on the key that owes it.

func (*PaymentTracker) PruneExpiredPayments added in v0.0.2

func (t *PaymentTracker) PruneExpiredPayments(currentEpoch phase0.Epoch)

PruneExpiredPayments removes pending payments older than 2 epochs.

func (*PaymentTracker) ReconcileToEpoch added in v0.0.3

func (t *PaymentTracker) ReconcileToEpoch(snapshotEpoch phase0.Epoch)

ReconcileToEpoch drops each key's local adjustment once the authoritative builder snapshot advances past the epoch the adjustment is anchored to: the newer snapshot already accounts for every reveal/top-up from earlier epochs. Deltas anchored to the snapshot's own epoch are retained (not yet reflected). Safe to call every refresh; a no-op until the epoch advances.

func (*PaymentTracker) RecordWonBid added in v0.0.2

func (t *PaymentTracker) RecordWonBid(keyIndex uint64, slot phase0.Slot, value uint64)

RecordWonBid records a won bid as a pending payment (unrevealed) against the key whose bid was included. If we later reveal, MarkRevealed moves it from pending to a balance deduction; otherwise it stays pending for 2 epochs and then expires.

func (*PaymentTracker) SetPaymentDisputed added in v0.0.3

func (t *PaymentTracker) SetPaymentDisputed(keyIndex uint64, slot phase0.Slot, disputed bool)

SetPaymentDisputed flags (or clears) a pending payment whose winning block was reorged out. An already settled payment (revealed and deducted) cannot be rolled back locally — the on-chain payment quorum decides its fate — so the dispute is only logged in that case.

type PendingPayment added in v0.0.2

type PendingPayment struct {
	KeyIndex uint64
	Slot     phase0.Slot
	Epoch    phase0.Epoch
	Value    uint64 // Gwei
	// Disputed marks a payment whose winning block was reorged out: it no
	// longer counts toward the pending total unless the block returns.
	Disputed bool
}

PendingPayment records an unrevealed won bid that may be deducted later.

type ProposerPreferencesCodec added in v0.0.2

type ProposerPreferencesCodec struct{}

ProposerPreferencesCodec translates the proposer preference store's entries to their persisted form: decimal slot string keys, SSZ-encoded values.

func (ProposerPreferencesCodec) DecodeKey added in v0.0.2

func (ProposerPreferencesCodec) DecodeKey(key string) (phase0.Slot, error)

DecodeKey parses a decimal slot string.

func (ProposerPreferencesCodec) DecodeValue added in v0.0.2

DecodeValue SSZ-decodes a signed proposer preference.

func (ProposerPreferencesCodec) EncodeKey added in v0.0.2

func (ProposerPreferencesCodec) EncodeKey(slot phase0.Slot) string

EncodeKey encodes a slot as its decimal string form.

func (ProposerPreferencesCodec) EncodeValue added in v0.0.2

EncodeValue SSZ-encodes a signed proposer preference.

type ProposerPreferencesService added in v0.0.2

type ProposerPreferencesService struct {
	// contains filtered or unexported fields
}

ProposerPreferencesService listens to the beacon node's proposer_preferences SSE topic, caches the first valid preference per slot, prunes old slots on epoch transitions, and resolves proposer settings for Gloas+ payload builds (it implements payload_builder.ProposerSettingsResolver).

func NewProposerPreferencesService added in v0.0.2

func NewProposerPreferencesService(clClient *beacon.Client, chainSvc chain.Service,
	log logrus.FieldLogger) *ProposerPreferencesService

NewProposerPreferencesService creates a new proposer preferences service. The backing store keeps the first valid preference per slot (subsequent preferences for the same slot are ignored).

func (*ProposerPreferencesService) GetStore added in v0.0.2

GetStore returns the underlying per-slot preference store for direct access (bid gating, bid construction, WebUI listing).

func (*ProposerPreferencesService) ResolveProposerSettings added in v0.0.2

ResolveProposerSettings resolves the proposer's announced settings for a build from the cached gossip preference. Self-scoped: it only applies from the Gloas fork onwards and returns false when no preference is cached for the slot.

func (*ProposerPreferencesService) Start added in v0.0.2

Start subscribes to the proposer_preferences SSE topic and to epoch stats (for pruning) and begins processing events in the service's own loop.

func (*ProposerPreferencesService) Stop added in v0.0.2

func (s *ProposerPreferencesService) Stop()

Stop stops the service and waits for the main loop to exit.

type RevealContext

type RevealContext struct {
	BuilderIndex          uint64
	BeaconBlockRoot       phase0.Root
	ParentBeaconBlockRoot phase0.Root

	// Transform, when set, is an operator-supplied jq expression applied to the
	// envelope MESSAGE (JSON) before signing; the modified message is then
	// signed, so the resulting envelope is validly signed but customized.
	Transform string
}

RevealContext are the inputs a transport supplies for a payload reveal that aren't carried by the payload: the builder index and the beacon block roots the envelope is bound to.

type RevealRequest added in v0.0.2

type RevealRequest struct {
	Payload *payload_builder.Payload
	// Key is the builder key whose bid the block committed to. The envelope
	// must be signed by it and carry its builder index, or the reveal is
	// rejected while the payment is still owed.
	Key       *builder_keys.Key
	BlockInfo *beacon.BlockInfo // root + parent root of the committing beacon block
	Transport payload_builder.BidTransport
}

RevealRequest asks the RevealService to publish a payload's envelope at the configured reveal time. Both flows submit these; the service dedupes by slot.

type RevealResult added in v0.0.2

type RevealResult struct {
	Slot        phase0.Slot
	Transport   payload_builder.BidTransport
	Success     bool
	Skipped     bool   // reveal was skipped without publishing (see SkipReason)
	SkipReason  string `json:"skip_reason,omitempty"` // RevealSkipReasonPlanDisabled | RevealSkipReasonLate
	Error       string // failure reason (when Success is false)
	Attempt     int    // 1-based
	MaxAttempts int

	// StartedAt/CompletedAt bracket the attempt (envelope construction +
	// submit call) — the submit call alone takes several hundred ms on some
	// clients, so the span matters. Zero on skips (nothing was attempted).
	StartedAt   time.Time
	CompletedAt time.Time

	// Envelope is the signed envelope built for the attempt. It is set from
	// construction onward on every attempt (including failed publishes) and
	// nil on skips and pre-construction failures.
	Envelope *eth2all.SignedExecutionPayloadEnvelope `json:"-"`
}

RevealResult reports the outcome of a reveal attempt.

type RevealService added in v0.0.2

type RevealService struct {
	// contains filtered or unexported fields
}

RevealService publishes execution payload envelopes once the slot's reveal gates open. It runs its own main loop: requests arrive on a channel, due times are awaited with a timer (no polling), head-vote updates open vote gates event-driven, reveals are deduped per slot (the first request wins regardless of transport), and failed publishes are retried per the configured retry policy. It is independent of the p2p bidder and Builder API modules and their enable flags. Each slot's reveal follows the slot's frozen action plan (suppression, gate mode, reveal time, vote threshold, broadcast validation, deadline bypass) — the plan service is the single per-slot settings authority.

func NewRevealService added in v0.0.2

func NewRevealService(
	cfg *config.Config,
	registry *builder_keys.Registry,
	publisher envelopePublisher,
	chainSvc chain.Service,
	builderSvc *payload_builder.Service,
	payments *PaymentTracker,
	planSvc *action_plan.PlanService,
	votes headVoteSource,
	log logrus.FieldLogger,
) *RevealService

NewRevealService creates a new reveal service. planSvc is required — every reveal decision resolves the slot's frozen action plan; passing nil is a programming error. votes is the head-vote tracker backing the vote gates; it may be nil (vote gates then never open and expire at the slot end).

func (*RevealService) RequestReveal added in v0.0.2

func (s *RevealService) RequestReveal(req *RevealRequest)

RequestReveal enqueues a reveal request; non-blocking: a full queue is logged and dropped (at most one reveal per slot, so the 16 buffer is generous).

func (*RevealService) Start added in v0.0.2

func (s *RevealService) Start(ctx context.Context) error

Start starts the reveal service's main loop.

func (*RevealService) Stop added in v0.0.2

func (s *RevealService) Stop()

Stop stops the reveal service and waits for the main loop to exit.

func (*RevealService) SubscribeResults added in v0.0.2

func (s *RevealService) SubscribeResults(capacity int, blocking bool) *utils.Subscription[*RevealResult]

SubscribeResults subscribes to reveal results (consumed by the WebUI).

func (*RevealService) SubscribeStarts added in v0.0.3

func (s *RevealService) SubscribeStarts(capacity int, blocking bool) *utils.Subscription[*RevealStarted]

SubscribeStarts subscribes to reveal attempt starts (consumed by the WebUI to render the in-flight submit call live).

type RevealStarted added in v0.0.3

type RevealStarted struct {
	Slot      phase0.Slot
	Transport payload_builder.BidTransport
	Attempt   int // 1-based
	StartedAt time.Time
}

RevealStarted announces the beginning of a reveal attempt (envelope construction + submit call) so the UI can render the in-flight call live; the matching RevealResult follows when the attempt completes.

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer signs execution payload bids and envelopes with the builder's BLS key.

func NewSigner

func NewSigner(blsSigner *signer.BLSSigner) *Signer

NewSigner creates a new payload bidder signer.

func (*Signer) SignBid

func (s *Signer) SignBid(
	bid *eth2all.ExecutionPayloadBid,
	forkVersion phase0.Version,
	genesisValidatorsRoot phase0.Root,
) (phase0.BLSSignature, error)

SignBid signs an execution payload bid. forkVersion must be the fork version the consensus client verifies against (the Gloas fork version).

func (*Signer) SignEnvelope

func (s *Signer) SignEnvelope(
	envelope *eth2all.ExecutionPayloadEnvelope,
	forkVersion phase0.Version,
	genesisValidatorsRoot phase0.Root,
) (phase0.BLSSignature, error)

SignEnvelope signs an execution payload envelope.

type WonBlock added in v0.0.2

type WonBlock struct {
	Source          string `json:"source"`
	Slot            uint64 `json:"slot"`
	BlockHash       string `json:"block_hash"`
	NumTransactions int    `json:"num_transactions"`
	NumBlobs        int    `json:"num_blobs"`
	ValueWei        string `json:"value_wei"`
	ValueETH        string `json:"value_eth"`
	Timestamp       int64  `json:"timestamp"` // Unix milliseconds at inclusion time
}

WonBlock is a block of ours that was included in a beacon block, won via either the Builder API or p2p ePBS bidding. The JSON tags are the wire shape consumed by the WebUI (bids-won REST endpoint and bid_won SSE event) — do not change them.

type WonBlockCodec added in v0.0.2

type WonBlockCodec struct{}

WonBlockCodec translates the won-block store's entries to their persisted form: decimal slot string keys, JSON-encoded values. JSON is deliberate: WonBlock is a local aggregate (not a spec SSZ type), and the kv_store value is an opaque blob either way.

func (WonBlockCodec) DecodeKey added in v0.0.2

func (WonBlockCodec) DecodeKey(key string) (phase0.Slot, error)

DecodeKey parses a decimal slot string.

func (WonBlockCodec) DecodeValue added in v0.0.2

func (WonBlockCodec) DecodeValue(value []byte) (*WonBlock, error)

DecodeValue JSON-decodes a won block.

func (WonBlockCodec) EncodeKey added in v0.0.2

func (WonBlockCodec) EncodeKey(slot phase0.Slot) string

EncodeKey encodes a slot as its decimal string form.

func (WonBlockCodec) EncodeValue added in v0.0.2

func (WonBlockCodec) EncodeValue(wonBlock *WonBlock) ([]byte, error)

EncodeValue JSON-encodes a won block.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL