payload_builder

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: 33 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultCacheSize is the number of slots to keep in the cache.
	DefaultCacheSize = 1000
)

Variables

This section is empty.

Functions

func ModifyPayloadExtraData

func ModifyPayloadExtraData(
	p *engineall.ExecutionPayload,
	executionRequests []prague.ExecutionRequest,
	extraDataPrefix []byte,
	parentBeaconBlockRoot common.Hash,
	gasLimitOverride uint64,
) (common.Hash, error)

ModifyPayloadExtraData rewrites the extraData field of a built execution payload in place, prepending the given prefix (truncating the original to stay within the 32-byte limit), recomputes the block hash, updates the payload's BlockHash field, and returns the new hash.

The parentBeaconBlockRoot is required because it is part of the block header (and therefore affects the block hash) but is not carried in the execution payload itself.

executionRequests carries the EIP-7685 execution requests from the engine API response (Electra/Prague+); they are needed to compute the requestsHash header field. Pass nil for pre-Electra payloads.

gasLimitOverride, when non-zero, additionally rewrites the payload's gas limit (part of the header, so it changes the block hash): used to enforce the exact bid-gossip-legal gas limit when the EL ignored the proposer's target. Callers must ensure the payload's gas usage fits the override.

The function first verifies it can reconstruct the original block hash from the payload fields. If verification fails (e.g. an unhandled fork added new header fields) it returns an error rather than producing an incorrect hash.

func ParseExecutionRequests

func ParseExecutionRequests(raw []prague.ExecutionRequest, dataVersion version.DataVersion) (*eth2all.ExecutionRequests, error)

ParseExecutionRequests decodes raw EIP-7685 execution request bytes from the Engine API into a versioned eth2all.ExecutionRequests.

Each element in raw is: [type_prefix_byte || request_1 || request_2 || ...] where all requests of the same type are concatenated after a single prefix byte. Builder deposit (0x03) and builder exit (0x04) requests are only valid from Gloas onwards.

Types

type BidRecord

type BidRecord struct {
	Transport        BidTransport
	Value            phase0.Gwei
	ExecutionPayment phase0.Gwei
	At               time.Time
}

BidRecord is a lightweight record of a bid produced for this payload (not the heavy payload itself).

type BidTransport

type BidTransport string

BidTransport identifies which submitter produced a bid or reveal.

const (
	// BidTransportP2P is the ePBS p2p gossip submitter.
	BidTransportP2P BidTransport = "p2p"
	// BidTransportBuilderAPI is the HTTP Builder API submitter.
	BidTransportBuilderAPI BidTransport = "builder-api"
)

type BlobsBundle

type BlobsBundle struct {
	Commitments []deneb.KZGCommitment `json:"commitments"`
	Proofs      []deneb.KZGProof      `json:"proofs"`
	Blobs       []deneb.Blob          `json:"blobs"`
}

BlobsBundle holds the blobs, KZG commitments and proofs produced alongside a payload, in beacon (deneb) types. The engine API bundle is converted to this once in the builder (see beaconBlobsBundleFromEngine) so downstream consumers use the beacon types directly instead of re-converting at every call site.

func (*BlobsBundle) BlobsAsBytes

func (b *BlobsBundle) BlobsAsBytes() [][]byte

BlobsAsBytes returns the blobs as raw byte slices for beacon submission. Nil-safe: returns nil for a nil bundle.

func (*BlobsBundle) MarshalJSON

func (b *BlobsBundle) MarshalJSON() ([]byte, error)

MarshalJSON renders the bundle as {commitments, proofs, blobs} hex arrays. Nil-safe: a nil bundle marshals to JSON null.

func (*BlobsBundle) ProofsAsBytes

func (b *BlobsBundle) ProofsAsBytes() [][]byte

ProofsAsBytes returns the KZG proofs as raw byte slices for beacon submission. Nil-safe: returns nil for a nil bundle.

type BuildSkippedEvent added in v0.0.3

type BuildSkippedEvent struct {
	Slot   phase0.Slot
	Reason string // one of the BuildSkipReason* constants
}

BuildSkippedEvent is emitted when the builder deliberately does not build for a slot that has a per-slot action plan or an effectively active consumer. Subscribers (e.g. the slot results tracker) use it to record explainable skips.

type BuilderStats

type BuilderStats struct {
	SlotsBuilt     uint64
	BidsSubmitted  uint64
	BidsWon        uint64
	BlocksIncluded uint64 // Blocks where our payload was included
	TotalPaid      uint64 // Gwei paid for won bids
	RevealsSuccess uint64
	RevealsFailed  uint64
	RevealsSkipped uint64
}

BuilderStats tracks statistics for builder operations.

type ELClientVersion

type ELClientVersion struct {
	Code    string
	Name    string
	Version string
	Commit  string
}

ELClientVersion is the execution client's identification, as returned by engine_getClientVersionV1, in display-friendly string form.

type EngineClient

type EngineClient interface {
	// ForkchoiceUpdatedAgnostic updates the forkchoice and optionally starts a
	// payload build, using the fork-agnostic request union.
	ForkchoiceUpdatedAgnostic(
		ctx context.Context,
		request *engineall.ForkchoiceUpdatedRequest,
	) (*paris.ForkchoiceUpdatedResponse, error)

	// GetPayloadAgnostic retrieves a built payload as the fork-agnostic union,
	// dispatching to the engine_getPayload version implied by dataVersion.
	GetPayloadAgnostic(
		ctx context.Context,
		dataVersion enginev.DataVersion,
		payloadID paris.PayloadID,
	) (*engineall.GetPayloadResponse, error)

	// ClientVersion exchanges client identity via engine_getClientVersionV1.
	ClientVersion(
		ctx context.Context,
		clientVersion *identification.ClientVersion,
	) ([]*identification.ClientVersion, error)
}

EngineClient is the subset of the go-eth-engine-client JSON-RPC service used by buildoor. It dispatches each fork-agnostic request to the matching versioned engine_* method internally. The jsonrpc.Service satisfies it.

type Payload

type Payload struct {
	// Attributes is the payload_attributes event this build was triggered by.
	Attributes *beacon.PayloadAttributesEvent
	// Candidate classifies the parent tuple this payload was built on
	// relative to the chain view at build time (parent/grandparent x
	// full/empty). Empty when the tuple matched no known candidate.
	Candidate chain.CandidateKey
	// ExecutionPayload is the fork-agnostic beacon execution payload.
	ExecutionPayload *eth2all.ExecutionPayload
	// BlobsBundle holds the blobs/commitments/proofs (Deneb+), nil if none.
	BlobsBundle *BlobsBundle
	// ExecutionRequests are the parsed execution requests (Electra+), versioned for the active fork.
	ExecutionRequests *eth2all.ExecutionRequests

	// Metadata not carried by the objects above.
	BlockHash    phase0.Hash32  // block hash after extra-data injection
	FeeRecipient common.Address // resolved proposer fee recipient for the bid
	BlockValue   *big.Int       // EL-reported block value (wei)
	ReadyAt      time.Time      // when the payload became ready
	// contains filtered or unexported fields
}

Payload is the canonical built payload, produced once by the builder and referenced (never copied) throughout the stack — it is a large object (blobs, transactions), so downstream consumers hold the same *Payload rather than copying it. The build outputs are immutable; the bid/reveal activity log is appended by the payload_bidder as bids are produced and the payload revealed.

Anything derivable from the build objects (slot, parent hashes, timestamp, gas limit, ...) is read through them rather than duplicated here.

func (*Payload) AddBid

func (p *Payload) AddBid(rec BidRecord)

AddBid appends a bid record to the payload's activity log.

func (*Payload) Bids

func (p *Payload) Bids() []BidRecord

Bids returns a snapshot copy of the bids recorded for this payload.

func (*Payload) MarkRevealed

func (p *Payload) MarkRevealed(rec RevealRecord)

MarkRevealed records the reveal of this payload's envelope. The first reveal wins; subsequent calls are ignored.

func (*Payload) Reveal

func (p *Payload) Reveal() *RevealRecord

Reveal returns the reveal record if the payload has been revealed, else nil.

type PayloadBuildFailedEvent

type PayloadBuildFailedEvent struct {
	Slot      phase0.Slot
	Candidate string    // candidate key the build targeted ("" = unclassified)
	Error     string    // Failure reason
	FailedAt  time.Time // When the build failed
}

PayloadBuildFailedEvent is emitted when a payload build fails. Subscribers (e.g. the WebUI) use it to mark the in-progress build as failed instead of leaving it rendered as perpetually building.

type PayloadBuildStartedEvent

type PayloadBuildStartedEvent struct {
	Slot      phase0.Slot
	Candidate string    // candidate key the build targets ("" = unclassified)
	StartedAt time.Time // When the build started
}

PayloadBuildStartedEvent is emitted when payload building begins for a slot, before the build has completed. Subscribers (e.g. the WebUI) use it to render the build as in-progress rather than waiting for the payload to be ready.

type PayloadBuilder

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

PayloadBuilder handles execution payload building via the Engine API.

func NewPayloadBuilder

func NewPayloadBuilder(
	clClient *beacon.Client,
	engineClient EngineClient,
	chainSvc chain.Service,
	feeRecipient common.Address,
	cfg *config.Config,
	log logrus.FieldLogger,
	settingsResolvers []ProposerSettingsResolver,
) *PayloadBuilder

NewPayloadBuilder creates a new payload builder. cfg is the shared config pointer; mutable settings (e.g. PayloadBuildTime) are read live from it. settingsResolvers are asked in order for the proposer's announced fee recipient and gas limit; the first match wins.

func (*PayloadBuilder) AbortBuild

func (b *PayloadBuilder) AbortBuild(slot phase0.Slot)

AbortBuild aborts every active build for the given slot.

func (*PayloadBuilder) BuildPayloadFromAttributes

func (b *PayloadBuilder) BuildPayloadFromAttributes(
	ctx context.Context,
	attrs *beacon.PayloadAttributesEvent,
	buildTimeMs uint64,
) (*Payload, error)

BuildPayloadFromAttributes builds a payload using data from a payload_attributes event. This is the primary build path, triggered when the beacon node emits payload_attributes. The event contains all necessary information: timestamp, randao, withdrawals, etc.

The attributes may be an effective copy with the parent fields redirected to another candidate parent (reorg / payload-miss handling); this method treats whatever parent it is given as authoritative and stores it on the returned Payload, so the bid built from that payload advertises the same parent it built on.

buildTimeMs is the EL build wait; 0 uses the live-configured PayloadBuildTime.

type PayloadCache

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

PayloadCache stores built payloads per slot, keyed by the parent tuple they were built on: reorg and payload-miss handling can produce several candidate payloads for the same slot.

func NewPayloadCache

func NewPayloadCache(maxSlots int) *PayloadCache

NewPayloadCache creates a new payload cache with the specified maximum slots.

func (*PayloadCache) Cleanup

func (c *PayloadCache) Cleanup(olderThan phase0.Slot)

Cleanup removes payloads older than the given slot.

func (*PayloadCache) Delete

func (c *PayloadCache) Delete(slot phase0.Slot)

Delete removes all payloads for the given slot.

func (*PayloadCache) Get

func (c *PayloadCache) Get(slot phase0.Slot) *Payload

Get retrieves the slot's primary payload: the most canonical classified candidate, falling back to the most recently built unclassified one.

func (*PayloadCache) GetAll

func (c *PayloadCache) GetAll() []*Payload

GetAll returns all cached payloads.

func (*PayloadCache) GetByBlockHash

func (c *PayloadCache) GetByBlockHash(blockHash phase0.Hash32) *Payload

GetByBlockHash retrieves a payload by its block hash.

func (*PayloadCache) GetCandidate added in v0.0.3

func (c *PayloadCache) GetCandidate(slot phase0.Slot, key chain.CandidateKey) *Payload

GetCandidate retrieves the slot's payload built for the given candidate key.

func (*PayloadCache) GetSlotPayloads added in v0.0.3

func (c *PayloadCache) GetSlotPayloads(slot phase0.Slot) []*Payload

GetSlotPayloads returns all payloads built for the slot (arbitrary order).

func (*PayloadCache) GetVariant added in v0.0.3

func (c *PayloadCache) GetVariant(slot phase0.Slot, key beacon.AttrParentKey) *Payload

GetVariant retrieves the slot's payload built on the given parent tuple.

func (*PayloadCache) Size

func (c *PayloadCache) Size() int

Size returns the number of slots with cached payloads.

func (*PayloadCache) Store

func (c *PayloadCache) Store(event *Payload)

Store stores a payload in the cache (replacing an earlier build on the same parent tuple) and evicts old slots to maintain the size limit.

type ProposerSettings added in v0.0.2

type ProposerSettings struct {
	FeeRecipient   common.Address
	TargetGasLimit uint64 // 0 = not announced
}

ProposerSettings are the proposer-announced build parameters for a slot.

type ProposerSettingsResolver added in v0.0.2

type ProposerSettingsResolver interface {
	ResolveProposerSettings(slot phase0.Slot, proposerIndex phase0.ValidatorIndex) (ProposerSettings, bool)
}

ProposerSettingsResolver resolves the proposer's announced settings for a build. Implementations self-scope: they return false when they don't apply to the slot's fork or hold no data (gossip preferences post-Gloas in payload_bidder; Builder API validator registrations pre-Gloas in the legacy dialect).

type RevealRecord

type RevealRecord struct {
	Transport       BidTransport
	BeaconBlockRoot phase0.Root
	At              time.Time
}

RevealRecord records that the payload's execution envelope was revealed.

type Service

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

Service is the standalone builder service that handles payload building. It does NOT handle ePBS bidding or revealing - those are handled by the epbs package.

Fork-aware building: - Electra/Fulu: Build on parent block (payload is in the block) - Gloas: Build on last known payload (payload is separate from block)

Building is triggered by payload_attributes events from the beacon node, which contain all the information needed to build a payload.

func NewService

func NewService(
	cfg *config.Config,
	clClient *beacon.Client,
	chainSvc chain.Service,
	planSvc *action_plan.PlanService,
	engineClient EngineClient,
	feeRecipient common.Address,
	log logrus.FieldLogger,
) (*Service, error)

NewService creates a new builder service. Proposer settings (fee recipient, target gas limit) are resolved through the resolvers registered via AddProposerSettingsResolver before Start. planSvc is the per-slot scheduling authority: every build decision polls it for the slot's frozen plan (schedule, force/suppress, build timing).

func (*Service) AddProposerSettingsResolver added in v0.0.2

func (s *Service) AddProposerSettingsResolver(r ProposerSettingsResolver)

AddProposerSettingsResolver appends a resolver; the builder asks each registered resolver in order and uses the first match. Register before Start().

func (*Service) BuildCandidateOnDemand added in v0.0.3

func (s *Service) BuildCandidateOnDemand(
	ctx context.Context, slot phase0.Slot, parentRoot phase0.Root, parentHash phase0.Hash32,
) (*Payload, error)

BuildCandidateOnDemand returns the slot's payload for the given parent tuple, building it on the fly when no candidate covers it yet (used by the Builder API to serve a proposer requesting a legal but unbuilt parent). Bounded by ctx; a tuple whose attributes cannot be resolved (unknown to the chain view and no received variant) fails.

func (*Service) GetCLClient

func (s *Service) GetCLClient() *beacon.Client

GetCLClient returns the consensus layer client.

func (*Service) GetChainSpec

func (s *Service) GetChainSpec() *chain.ChainSpec

GetChainSpec returns the chain specification.

func (*Service) GetConfig

func (s *Service) GetConfig() *config.Config

GetConfig returns the current configuration.

func (*Service) GetCurrentSlot

func (s *Service) GetCurrentSlot() phase0.Slot

GetCurrentSlot returns the most recently built slot.

func (*Service) GetELClientVersion

func (s *Service) GetELClientVersion() *ELClientVersion

GetELClientVersion returns the cached EL client identification. Returns nil if the EL has not yet responded or does not support engine_getClientVersionV1.

func (*Service) GetGenesis

func (s *Service) GetGenesis() *beacon.Genesis

GetGenesis returns the genesis information.

func (*Service) GetPayloadCache

func (s *Service) GetPayloadCache() *PayloadCache

GetPayloadCache returns the payload cache for direct access.

func (*Service) GetStats

func (s *Service) GetStats() BuilderStats

GetStats returns the current builder statistics.

func (*Service) IncrementBidsSubmitted

func (s *Service) IncrementBidsSubmitted()

IncrementBidsSubmitted increments the bids submitted counter. Called by the ePBS service when a bid is submitted.

func (*Service) IncrementBlocksIncluded

func (s *Service) IncrementBlocksIncluded()

IncrementBlocksIncluded increments the blocks included and bids won counters. Called by the ePBS service when our payload is included in a beacon block.

func (*Service) IncrementRevealsFailed

func (s *Service) IncrementRevealsFailed()

IncrementRevealsFailed increments the failed reveals counter.

func (*Service) IncrementRevealsSkipped

func (s *Service) IncrementRevealsSkipped()

IncrementRevealsSkipped increments the skipped reveals counter.

func (*Service) IncrementRevealsSuccess

func (s *Service) IncrementRevealsSuccess()

IncrementRevealsSuccess increments the successful reveals counter.

func (*Service) Start

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

Start initializes and starts the builder service.

func (*Service) Stop

func (s *Service) Stop()

Stop stops the builder service.

func (*Service) SubscribeBuildSkipped added in v0.0.3

func (s *Service) SubscribeBuildSkipped(
	capacity int,
	blocking bool,
) *utils.Subscription[*BuildSkippedEvent]

SubscribeBuildSkipped subscribes to build skipped events. Authoritative consumers (the slot results tracker) should pass blocking=true so no skip is lost; informational consumers should pass blocking=false.

func (*Service) SubscribePayloadBuildFailed

func (s *Service) SubscribePayloadBuildFailed(
	capacity int, blocking bool,
) *utils.Subscription[*PayloadBuildFailedEvent]

SubscribePayloadBuildFailed subscribes to payload build failed events. Consumers (like the WebUI) use this to mark in-progress builds as failed.

func (*Service) SubscribePayloadBuildStarted

func (s *Service) SubscribePayloadBuildStarted(
	capacity int, blocking bool,
) *utils.Subscription[*PayloadBuildStartedEvent]

SubscribePayloadBuildStarted subscribes to payload build started events. Consumers (like the WebUI) use this to render builds as in-progress.

func (*Service) SubscribePayloadReady

func (s *Service) SubscribePayloadReady(capacity int, blocking bool) *utils.Subscription[*Payload]

SubscribePayloadReady subscribes to payload ready events. Consumers (like the ePBS service) use this to receive built payloads.

func (*Service) UnmarkPayloadWon added in v0.0.3

func (s *Service) UnmarkPayloadWon(blockHash phase0.Hash32)

UnmarkPayloadWon clears the won marker of a payload whose winning block was reorged out, so a later re-inclusion is detected (and counted) again.

func (*Service) UpdateConfig

func (s *Service) UpdateConfig(cfg *config.Config) error

UpdateConfig updates the service configuration at runtime. Schedule changes are handled by the plan service (the scheduling authority).

Jump to

Keyboard shortcuts

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