Learning Gloas Through Lodestar

This guide is for readers who already understand Ethereum's proof-of-stake protocol—slots, proposers, attestations, the beacon chain, and the execution layer—and want to go all the way down: reading the Gloas consensus specification itself, function by function, until every container, constant, and handler makes sense.

The path is deliberate. First build the right mental model, because the Gloas spec is hard to read cold. Then learn how the spec documents are organized and which functions carry the design. Finally, read each spec function next to Lodestar's implementation of it: the specification defines what must happen, Lodestar shows how a production client does it, and the tests show what breaking each rule looks like. Reading all three together is the fastest way to make the spec stick.

What you will learn

By the end of this guide, you should be able to explain:

  1. Why Gloas separates a beacon block from its execution payload.
  2. What proposers, builders, ordinary attesters, and the Payload Timeliness Committee (PTC) do during a slot.
  3. Why one beacon block root can have PENDING, EMPTY, and FULL fork-choice variants.
  4. Why a child beacon block applies its parent's execution-payload effects.
  5. How in-protocol builder bids and payments work at a high level.
  6. How the five Gloas spec documents divide the protocol, and which spec functions carry the design.
  7. Where each idea lives in Lodestar, how to move between a spec function and its implementation with specrefs, and how to test it.

Prerequisite refresher

You only need the following mental model before starting:

If any of those points are unfamiliar, first read the Phase 0 beacon-chain and fork-choice introductions, then return here.

Gloas in one sentence

Gloas makes the beacon block and execution payload two separately propagated objects, while adding protocol rules that bind them together, choose between a present and missing payload, and enforce a selected builder's payment.

The main proposal behind this design is EIP-7732, Enshrined Proposer-Builder Separation, usually shortened to ePBS.

The Gloas bundle in the pinned spec also includes:

Most of this guide focuses on ePBS because it causes the largest conceptual and architectural change.

How ePBS and Gloas relate

ePBS and Gloas are not two protocols that run one after the other:

Therefore, the “ePBS flow” below happens under the Gloas rules. The other Gloas changes modify parts of the payload and state transition, but they do not form a separate second flow.

How the Gloas spec is organized

Going deep means reading the specification itself, so learn its shape early. The consensus-specs repository is an executable Python specification—the “pyspec.” Every function you will read can be run, and the spec test vectors that Lodestar consumes are generated from it.

Gloas is specified in five documents under specs/gloas/. Each answers a different question:

DocumentQuestion it answers
beacon-chain.mdWhat are the containers, constants, and the deterministic state transition?
fork-choice.mdHow do received blocks, envelopes, and PTC messages change the head?
p2p-interface.mdWhich gossip topics and req/resp methods exist, and when is a message invalid?
validator.mdWhat should an honest proposer, attester, and PTC member do, and when?
builder.mdWhat should an honest builder do with bids and envelopes?

Two conventions make the spec much faster to read once you know them:

  1. Each fork is written as a diff. The Gloas documents restate only what changed relative to Fulu and mark functions with [New in Gloas:EIP7732] and [Modified in Gloas:EIP7732] annotations. If a function does not appear in the Gloas document, its latest earlier definition still applies.
  2. Numbers live outside the documents. Preset values such as PTC_SIZE are defined per preset (presets/mainnet, presets/minimal), and per-network values such as GLOAS_FORK_EPOCH live under configs/. Lodestar mirrors both in packages/params.

Two signing domains are new in Gloas, and they identify the new actors: DOMAIN_BEACON_BUILDER (0x0B000000) is used for bids and payload envelopes, and DOMAIN_PTC_ATTESTER (0x0C000000) is used for PTC payload attestations.

The key change: two publications instead of one

Before Gloas, the beacon block body includes the execution payload, blob KZG commitments, and execution requests. Consensus propagation and execution data therefore share the same block-publication path.

In Gloas, the beacon block carries a signed bid commitment instead. The full payload and its related execution data arrive later in a separately signed execution payload envelope.

Before GloasIn Gloas
BeaconBlockBody.executionPayloadBeaconBlockBody.signedExecutionPayloadBid
Blob commitments in the beacon blockBlob commitments in the bid; payload data travels separately
Execution requests in the beacon blockRequests in the envelope and repeated as parentExecutionRequests by the child
One block root represents the block and embedded payloadOne beacon block root can have pending, empty, and full payload views
Builders and payments are external to consensusStaked builders and payment obligations are tracked in beacon state

This separation shortens the consensus-critical path. Attesters can process the smaller beacon block before the ordinary attestation deadline, while the payload and data-availability work can use more of the slot.

The actors and their messages

Beacon proposer

The beacon proposer chooses a valid builder bid or self-builds. It publishes the beacon block near the start of the slot.

For an external build, the proposer publishes the bid commitment but does not publish the builder's envelope. For a self-build, the Lodestar validator client publishes both the beacon block and a proposer-signed envelope.

Builder

A Gloas builder is a staked protocol actor, but not a validator. A builder does not attest, propose beacon blocks, join sync committees, or earn validator yield.

An active builder may:

  1. Publish a SignedExecutionPayloadBid committing to an execution block and a payment.
  2. If selected, reveal the matching SignedExecutionPayloadEnvelope and its data before the payload deadline.

Ordinary attesters

Ordinary beacon attestations still drive LMD-GHOST weight. In Gloas, AttestationData.index also signals which payload view the attester supports:

This last rule is easy to miss. At the ordinary attestation deadline, the payload reveal window is still open, so same-slot attesters support the beacon block without deciding the payload outcome.

Payload Timeliness Committee

The PTC is a separate committee that the spec's get_ptc selects for each slot. Its size is the PTC_SIZE preset: 512 on mainnet and 16 on minimal in this snapshot.

Each member publishes a PayloadAttestationMessage, signed with the DOMAIN_PTC_ATTESTER domain and containing two independent observations:

PTC members do not need to fully execute the payload before voting. Their job is to report timely reveal and data availability. Full execution validation remains the EL's responsibility.

Beacon node and execution engine

The beacon node validates gossip, reconstructs the Gloas state associated with the beacon block, verifies the envelope's commitments and signature, waits for the required data columns, and asks the execution engine to verify the payload.

Importantly, envelope import does not mutate the canonical beacon state. The next beacon block applies the parent payload's state effects.

End-to-end ePBS flow under Gloas

Follow one externally built payload for slot N. This is the complete lifecycle, including the setup before the slot and the state effects after it.

Stage 0: the builder becomes eligible

This does not happen every slot. A prospective builder deposits through an EIP-8282 builder deposit request. The request first travels in an execution payload, then a later beacon block carries it as a parent execution request. After the deposit is recorded and its deposit epoch is finalized, the builder can submit protocol bids backed by its builder balance.

This registration is what makes an external builder's bid and payment enforceable by consensus rather than only by an off-protocol relay agreement.

Stage 1: prepare the parent and proposer preferences

Before slot N, nodes know the beacon parent and the latest usable execution hash. If the preceding Gloas beacon block has both full and empty variants, fork choice and shouldBuildOnFull determine which execution history should be extended.

The slot N proposer publishes signed proposer preferences, including its fee recipient and target gas limit. Builders bind their bids to these preferences and to the relevant chain history.

Stage 2: builders bid

Each builder constructs an execution payload candidate and gossips a SignedExecutionPayloadBid. The bid commits to the payload hash, execution parent hash, blob commitments, execution-requests root, builder identity, and payment value without publishing the full payload.

Lodestar rejects bids that do not match the slot, parent, proposer preferences, gas-limit rules, builder signature, builder status, or available builder balance. Valid bids enter the bid pool.

Stage 3: the proposer publishes the beacon block

At the start of slot N, the proposer selects a valid external bid or the self-build fallback. It publishes a SignedBeaconBlock containing the selected bid, but not the full execution payload.

Processing this block:

  1. resolves and applies the slot N-1 parent payload, if full;
  2. processes withdrawals and records those expected in the slot N payload if the parent is full, or pauses withdrawal processing if the parent is empty;
  3. reserves the selected builder's payment; and
  4. creates PENDING and EMPTY fork-choice variants for the slot N beacon root.

Notice the one-slot pipeline: block N resolves payload N-1 while committing to payload N.

Stage 4: ordinary attestations support the beacon block

Near 25% of slot N, ordinary attesters vote for the beacon block. Because the payload reveal window is still open, a same-slot attestation uses index 0 and supports the PENDING variant. It does not yet declare the payload empty.

When these attestations are later included, their validator balance contributes to the selected builder payment's support weight.

Stage 5: the selected builder reveals the payload

The winning builder learns the beacon block root that selected its bid, signs a matching SignedExecutionPayloadEnvelope, and publishes the envelope and data.

Two activities now proceed independently:

The PTC does not wait for full EL execution. If a node imports an accepted or optimistically accepted envelope, fork choice gains the slot N FULL variant. The envelope still does not mutate canonical beacon state.

Stage 6: the next proposer resolves full versus empty

At slot N+1, the next proposer combines its local payload knowledge with the received PTC observations. Its beacon block includes those observations as payloadAttestations aggregates:

A locally unavailable payload or a strict majority of explicit negative PTC observations for timeliness or data availability makes Lodestar build on the empty parent. PTC non-participation is not an explicit negative vote.

When nodes process block N+1, processParentExecutionPayload reads the new bid's parentBlockHash and deterministically applies the chosen result:

Slot N resultWhat block N+1 does
FULLApplies payload N's execution requests, marks it available, advances latestBlockHash, and settles its builder payment
EMPTYApplies no payload N requests and leaves the execution history at the earlier hash

This child-block step is the bridge between asynchronous envelope propagation and a deterministic consensus state transition.

Stage 7: payment and eventual finality

For a full parent, settling the payment moves the builder's obligation toward an execution withdrawal. For an empty parent, sufficiently strong ordinary attestation support can still make the payment withdrawable during epoch processing. When that withdrawal is included in a later execution payload, the builder balance is actually debited and the proposer's fee recipient receives the value.

Block N+1 choosing full or empty is not finality. The resulting beacon chain continues through normal LMD-GHOST and Casper FFG justification and finalization.

The recurring pipeline is:

block N-1 selects payload N-1
        ↓
block N resolves payload N-1 and selects payload N
        ↓
envelope N and PTC observations arrive
        ↓
block N+1 resolves payload N and selects payload N+1
        ↓
ordinary proof-of-stake finality continues

Slot timing for the central part of the flow

On a 12-second slot, the configured Gloas deadlines in this snapshot are:

Slot pointApproximate timeActivity
Start0 secondsProposer publishes the beacon block containing a bid
25%3 secondsOrdinary attestations and sync messages are due
50%6 secondsAttestation aggregates and sync contributions are due
75%9 secondsPayload and PTC payload attestations are due
Next slot12 secondsThe next proposer chooses whether to extend the full or empty parent

The order is more important than the exact numbers. Timing constants can change while Gloas is under development.

sequenceDiagram
    participant Proposer as Slot N proposer
    participant Builder
    participant BN as Beacon nodes
    participant EL as Execution engines
    participant PTC
    participant Next as Slot N+1 proposer

    Proposer->>BN: SignedProposerPreferences (sent ahead of slot N)
    Builder->>BN: SignedExecutionPayloadBid
    Proposer->>BN: SignedBeaconBlock with selected bid
    BN-->>BN: Create PENDING and EMPTY fork-choice variants
    BN-->>BN: Ordinary attestations support PENDING at 25%
    Builder->>BN: SignedExecutionPayloadEnvelope and data
    par Payload observation
        PTC->>BN: PayloadAttestationMessage at 75%
    and Full payload import
        BN->>EL: Verify payload
        BN-->>BN: Create FULL variant when accepted
    end
    alt Choose FULL
        Next->>BN: Block N+1 bid extends payload N blockHash
        BN-->>BN: Apply payload N effects and settle payment
    else Choose EMPTY
        Next->>BN: Block N+1 bid extends earlier execution hash
        BN-->>BN: Skip payload N effects; payment may settle by attestation weight
    end
    BN-->>BN: Continue normal justification and finalization

Builder bids may arrive for the current or next slot, and proposer preferences are normally propagated farther in advance. The sequence diagram emphasizes the dependency order rather than an exact networking schedule.

Failure branches at a glance

What happens in slot NLocal fork-choice resultWhat the slot N+1 child applies
Timely envelope, data available, payload accepted, no negative PTC majorityFULL can be selectedPayload requests, execution hash, and direct payment settlement
Builder withholds the envelopeOnly PENDING/EMPTY are usableNo payload effects; payment may still be earned from ordinary attestation support
Envelope or commitment is invalidEnvelope is rejected; no valid FULL variant from itEmpty-parent path
EL reports the payload invalidFull execution branch is invalidatedEmpty-parent or another viable chain
Envelope is known but strict-majority PTC evidence says late or unavailableshouldBuildOnFull chooses EMPTY for the recent parentNo payload effects

For a self-build, the overall pipeline is the same, but the proposer creates and publishes both objects. Lodestar uses the self-build builder-index sentinel, a zero bid value, and the point-at-infinity bid signature, so there is no external builder payment to enforce.

The five messages to know

You can understand most of ePBS by learning these messages in order.

1. SignedProposerPreferences

The future proposer states its desired:

Builders need matching preferences before their trustless bids are accepted. Lodestar's validator client submits them ahead of the proposal slot and resubmits them if the dependent root changes after a reorganization.

2. SignedExecutionPayloadBid

The bid commits to the proposed payload without revealing the full payload. The most useful fields are:

FieldMeaning
parentBlockRootParent beacon block root
parentBlockHashExecution block hash the new payload extends
blockHashPromised hash of the new execution payload
builderIndexRegistered builder, or the self-build sentinel
slotSlot for the proposed payload
valueBuilder's promised in-protocol payment in Gwei
blobKzgCommitmentsCommitment to the payload's blob data
executionRequestsRootCommitment to the payload's execution requests

For self-builds, Lodestar represents the UINT64_MAX builder-index sentinel as Infinity. The value must be zero and the bid signature is the BLS point at infinity.

3. SignedBeaconBlock

The Gloas body removes executionPayload, blobKzgCommitments, and executionRequests. It adds:

4. SignedExecutionPayloadEnvelope

The envelope reveals:

The envelope must match every relevant bid commitment, including block hash, RANDAO, gas limit, builder index, and execution-requests root.

5. PayloadAttestationMessage

This is an individual PTC member's signed observation. The next proposer groups messages with identical data into PayloadAttestation aggregates and includes them in the next beacon block.

Do not confuse roots and hashes

Most early Gloas confusion comes from mixing the CL and EL identifiers.

Suppose beacon block B[N] selects execution payload E[N]:

IdentifierPoints to
B[N].parentRootParent beacon block B[N-1]
bid.parentBlockRootAlso B[N-1]
bid.parentBlockHashExecution parent chosen for E[N]
bid.blockHashThe promised E[N]
envelope.beaconBlockRootThe beacon block B[N] that selected the bid
envelope.parentBeaconBlockRootB[N-1]

If B[N-1] is treated as FULL, bid.parentBlockHash normally points to E[N-1]. If B[N-1] is treated as EMPTY, it points to the last execution block before E[N-1]. The beacon ancestry may advance while the execution ancestry stays at the earlier hash.

Beacon state changes

Start your Lodestar code reading with packages/types/src/gloas/sszTypes.ts.

The new state fields fit into three groups.

Execution-payload tracking

Builder accounting

PTC lookahead

At the Fulu-to-Gloas fork, upgradeStateToGloas copies retained fields, initializes the new payload and PTC state, and onboards eligible pending builder deposits.

The state transition's central idea

The best file for seeing the new block-processing order is packages/state-transition/src/block/index.ts.

For Gloas, the spec's process_block reads:

def process_block(state: BeaconState, block: BeaconBlock) -> None:
    # [New in Gloas:EIP7732]
    process_parent_execution_payload(state, block)
    process_block_header(state, block)
    # [Modified in Gloas:EIP7732]
    process_withdrawals(state)
    # Removed `process_execution_payload`
    # [New in Gloas:EIP7732]
    process_execution_payload_bid(state, block.body.signed_execution_payload_bid)
    process_randao(state, block.body)
    process_eth1_data(state, block.body)
    # [Modified in Gloas:EIP7732]
    process_operations(state, block.body)
    process_sync_aggregate(state, block.body.sync_aggregate)

Lodestar mirrors this order with processParentExecutionPayload, processBlockHeader, processWithdrawals, and processExecutionPayloadBid before RANDAO, Eth1 data, operations, and sync-aggregate processing.

Note what is not here: process_execution_payload is removed from block processing. It still exists in Gloas, but it runs against the beacon block's post-state when the payload envelope arrives, not as part of the block.

Why process the parent payload in the child?

The payload envelope is a separately gossiped object. Gossip arrival must not directly mutate canonical beacon state because nodes can receive objects in different orders and can disagree temporarily about the head.

The child block resolves the ambiguity. Its bid says which execution hash it extends:

For a full parent, processParentExecutionPayload verifies that parentExecutionRequests hashes to the root committed by the parent bid. It then:

For an empty parent, parentExecutionRequests must be empty and none of those effects are applied.

This is the most important Gloas state-transition invariant:

An envelope can be received and verified during slot N, but its consensus state effects become canonical only when a later beacon block extends its full variant.

Why withdrawals are asynchronous

The consensus state computes and deducts expected withdrawals while processing the beacon block. It stores the result in payloadExpectedWithdrawals. When the envelope arrives, Lodestar verifies that the payload contains exactly those withdrawals.

If the parent is empty, withdrawal processing pauses rather than creating a new set that no execution payload has fulfilled.

Envelope import in Lodestar

On the spec side, envelope arrival is two functions: the fork-choice handler on_execution_payload_envelope and the state-transition function process_execution_payload, which in Gloas verifies the envelope against the selecting block's post-state. In Lodestar, that one spec event is split across validation, data waiting, import, persistence, and fork-choice updates—which is why process_execution_payload#gloas has no single source entry in specrefs.

Read verifyExecutionPayloadEnvelope.ts followed by importExecutionPayload.ts.

The import pipeline:

  1. Waits for the required data columns.
  2. Finds the corresponding beacon block in fork choice.
  3. Regenerates the block's post-state.
  4. Verifies the envelope against that state and its bid.
  5. Verifies the BLS signature and asks the EL to verify the payload.
  6. Persists the envelope.
  7. Adds a FULL fork-choice variant.
  8. Notifies the EL of the updated fork-choice head when appropriate.

The implementation can ask the EL to validate as soon as the envelope and data are available. “Delayed execution validation” means the ordinary attestation critical path does not wait for that result; it does not require clients to remain idle until the next slot.

Fork choice: one root, three variants

Before Gloas, Lodestar's proto-array has one node per beacon block root. The payload is embedded, so the block is effectively always FULL.

After Gloas, the same root can represent three payload states:

Lodestar models the local shape as:

parent EMPTY or FULL
        |
     PENDING   <- same-slot beacon attestations support this
      /   \
   EMPTY  FULL <- FULL appears when the envelope is imported

The central implementation is packages/fork-choice/src/protoArray/protoArray.ts.

How the variants are used

The spec's fork-choice document defines a handler per arriving object, and each handler maps onto the variant tree:

  1. onBlock (spec on_block) creates PENDING and EMPTY.
  2. onExecutionPayload (spec on_execution_payload_envelope) adds FULL as a sibling of EMPTY.
  3. Same-slot ordinary attestations vote for PENDING.
  4. A later ordinary attestation uses index 0 or 1 to add LMD weight to EMPTY or FULL.
  5. PTC messages arrive through on_payload_attestation_message (and notify_ptc_messages for aggregates carried in blocks). They record timely-reveal and data-availability observations that influence the recent-slot payload-status choice but do not replace ordinary LMD votes.
  6. A child bid's parentBlockHash declares which parent variant its execution payload extends; the spec's is_parent_node_full predicate reads exactly this comparison.

The spec's get_head then walks this three-variant tree instead of the flat one-node-per-root structure used before Gloas.

shouldBuildOnFull

Lodestar's shouldBuildOnFull implements the spec's should_build_on_full (read it together with the neighboring should_extend_payload and is_payload_verified in fork-choice.md).

For a full head from the immediately preceding slot, Lodestar checks the PTC's negative timeliness and data-availability votes. A strict majority of explicit “no” observations forces the next proposer to build on EMPTY.

Non-participation is distinct from a negative vote. The proto-array therefore tracks three bitsets: attendance, payload-present votes, and data-available votes.

The unit tests in packages/fork-choice/test/unit/protoArray/gloas.test.ts are an unusually good executable explanation of these rules.

Builder lifecycle and payments

Registration and activation

EIP-8282 adds builder deposit and exit request types to ExecutionRequests.

A new builder record contains a BLS pubkey, builder version, execution address, balance, deposit epoch, and withdrawable epoch. A builder becomes active only after its deposit epoch is finalized.

In the spec, the entry and exit paths are process_builder_deposit_request, add_builder_to_registry, process_builder_exit_request, and the is_active_builder predicate. The main Lodestar functions are in packages/state-transition/src/util/gloas.ts, processBuilderDepositRequest.ts, and processBuilderExitRequest.ts.

Bid validation

processExecutionPayloadBid checks the selected bid again during the state transition. An external builder must be active, have the payload-builder version, cover the bid plus existing obligations, and provide a valid BLS signature. The bid must also match the slot, parent roots and hashes, RANDAO, and blob limits.

Gossip validation additionally connects the bid to proposer preferences, target gas limit, known parent variants, and the best bid already seen for that parent.

Payment lifecycle

It is useful to think of a bid payment as moving through three stages:

  1. Reserved: processing the bid records a BuilderPendingPayment. canBuilderCoverBid prevents the builder from promising the same balance repeatedly.
  2. Earned: same-slot ordinary beacon attestations add balance weight to the payment. This is ordinary attestation weight, not PTC weight.
  3. Withdrawable: extending the parent's full payload settles the payment directly. Otherwise, epoch processing (process_builder_pending_payments, using the get_builder_payment_quorum_threshold support threshold) can accept a sufficiently supported payment and move it to builderPendingWithdrawals.

The actual builder balance debit happens when the resulting withdrawal is applied. This ordering makes the obligation enforceable while fitting builder payments into the existing execution-withdrawal mechanism.

A builder may still owe the proposer after withholding its payload. That is the “proposer unconditional payment” property: an accepted, sufficiently supported beacon block is not a free option for the builder.

Exit

A builder exit is authorized by the registered execution address. Lodestar ignores the request unless the builder is active, the source address matches, and there is no pending balance to withdraw. After exit initiation, the builder must wait through the configured withdrawability delay.

Two complete examples

Example A: timely external builder

Assume slot N selects builder 42's bid.

  1. The slot N beacon block is published with builder 42's signed bid.
  2. Beacon nodes create PENDING and EMPTY variants for its root.
  3. Same-slot attesters support PENDING and add weight to the payment.
  4. Builder 42 publishes a matching envelope and data before the deadline.
  5. PTC members report payloadPresent = true and blobDataAvailable = true according to their views.
  6. In parallel, nodes verify the commitments, signature, data availability, and EL result.
  7. Fork choice gains a FULL variant after successful payload import.
  8. The slot N+1 proposer builds on the full execution hash, includes the parent's execution requests, and aggregates the PTC messages.
  9. While processing block N+1, Lodestar applies slot N's requests, settles the builder payment, marks the payload available, and advances latestBlockHash.

Example B: builder withholds the payload

  1. The slot N beacon block is still valid and receives ordinary attestations.
  2. No matching envelope arrives on time, so no FULL variant is available.
  3. PTC members report payloadPresent = false according to their views.
  4. The next proposer builds on the EMPTY variant. Its bid extends the earlier execution hash, not the withheld payload's promised hash.
  5. parentExecutionRequests is empty, so the withheld payload's transactions and requests have no canonical effect.
  6. The builder's payment obligation can still become withdrawable if the beacon block received enough ordinary attestation support.

The consensus chain advanced at slot N, but the execution chain did not. This is why “empty” does not mean “skipped beacon slot.”

Normal attestations versus PTC attestations

PropertyOrdinary beacon attestationPTC payload attestation
Main purposeVote for consensus head and checkpointsReport payload timeliness and blob-data availability
CommitteeBeacon attestation committeePayload Timeliness Committee
Main payload signalAttestationData.index selects pending/empty/full semanticspayloadPresent and blobDataAvailable booleans
Fork-choice effectAdds LMD weightInfluences recent full/empty decision rules
Builder-payment effectSame-slot support adds payment weightNo direct payment weight
Deadline in this snapshot25% of slot75% of slot
Requires full EL execution first?No, same-slot vote is pendingNo

Where Gloas lives in Lodestar

Read these files in this order. Do not begin with the full beacon-node search results; Gloas touches too many files for that to be useful on a first pass.

StepTopicLodestar entry point
1Fork name and constantsforkName.ts, params/index.ts
2SSZ messages and stategloas/sszTypes.ts
3Fork upgradeupgradeStateToGloas.ts
4Block-processing orderstate-transition/src/block/index.ts
5Parent payload effectsprocessParentExecutionPayload.ts
6Bid and PTC operationsprocessExecutionPayloadBid.ts, processPayloadAttestation.ts
7Builder helpers and accountingutil/gloas.ts, processBuilderPendingPayments.ts
8Envelope verification and importverifyExecutionPayloadEnvelope.ts, importExecutionPayload.ts
9Full/empty fork choiceprotoArray.ts
10Block productionproduceBlockBody.ts
11Validator dutiesvalidator/services/block.ts, ptc.ts, proposerPreferences.ts
12Gossip and req/respnetwork/gossip/topic.ts, network/reqresp/types.ts

The spec functions to know

The five messages tell you what travels; these functions tell you what the protocol does with it. Together they cover most of the Gloas diff. Read them in the pinned spec, in roughly this order.

In beacon-chain.md

FunctionRole
process_parent_execution_payloadApplies the parent payload's effects inside the child block
process_withdrawalsComputes expected withdrawals; pauses them on an empty parent
process_execution_payload_bidValidates the selected bid and reserves the builder payment
process_payload_attestationProcesses the PTC aggregates included in a block
process_execution_payloadVerifies the revealed envelope against the block's post-state
process_builder_deposit_request, process_builder_exit_requestBuilder registry entry and exit
process_builder_pending_paymentsEpoch step that promotes supported payments to withdrawals
get_ptc, is_valid_indexed_payload_attestationPTC selection and payload-attestation signature checking
can_builder_cover_bid, get_builder_payment_quorum_thresholdBuilder balance-coverage and payment-quorum rules

In fork-choice.md

FunctionRole
on_blockAdds a block; in Gloas, seeds the pending and empty variants
on_execution_payload_envelopeAdds the full variant when an envelope is imported
on_payload_attestation_message, notify_ptc_messagesRecord PTC observations in the store
should_build_on_full, should_extend_payloadDecide whether the next proposal extends the full parent
is_parent_node_full, is_payload_verifiedPredicates connecting bids to parent variants
get_headHead selection over the three-variant tree

Then read validator.md for when each message is published—including get_ptc_assignment for PTC duty discovery—and builder.md for the honest builder's bid and reveal rules. p2p-interface.md defines the gossip topics these messages travel on and the exact conditions for rejecting or ignoring each one; Lodestar's gossip validation code follows those condition lists almost clause by clause.

Use specrefs as the bridge

Lodestar's specrefs/ directory records the pinned spec text and its source mapping. It is the fastest way to move from a Python spec function to TypeScript. functions.yml and containers.yml are the two you will use most, but constants, presets, configs, and dataclasses have their own files. The mapping is maintained with ethspecify, and each entry embeds the pinned spec text, so specrefs also works offline as a searchable copy of the Gloas spec.

For example:

rg -n -A 45 -- '^- name: process_execution_payload_bid#gloas' specrefs/functions.yml

The entry shows:

Use this repeatable study loop:

  1. Read one function in the pinned consensus spec.
  2. Find its entry in specrefs/functions.yml or its container in specrefs/containers.yml.
  3. Read the mapped TypeScript function.
  4. Search for its callers with rg.
  5. Read a positive and negative test for the same behavior.

Some high-level spec events are split across several Lodestar components and therefore have no single source in specrefs. Envelope arrival is one example: validation, data waiting, import, persistence, and fork-choice updates live in different files.

Suggested six-session study plan

Each session starts in the specification and ends in Lodestar. Reading the spec text first, while it is still slightly confusing, is deliberate: the implementation then answers the exact questions the spec raised.

Session 1: mental model and containers

Read this guide through “Do not confuse roots and hashes.” Then read the container definitions in the pinned beacon-chain.md and compare the Fulu and Gloas BeaconBlockBody and BeaconState, both in the spec and in sszTypes.ts.

Goal: draw the five messages and label every CL root and EL hash.

Session 2: deterministic state transition

Read process_block, process_parent_execution_payload, process_execution_payload_bid, and process_withdrawals in beacon-chain.md. Then read their implementations: block/index.ts, processParentExecutionPayload.ts, processExecutionPayloadBid.ts, and processWithdrawals.ts.

Goal: explain why the child carries parentExecutionRequests and why envelope gossip cannot mutate beacon state.

Session 3: fork choice

Read the store changes and handlers in fork-choice.mdon_block, on_execution_payload_envelope, on_payload_attestation_message, and should_build_on_full. Then read protoArray/interface.ts, the Gloas sections of protoArray.ts, and the first half of gloas.test.ts.

Goal: draw the parent links between two consecutive blocks when the first block has both empty and full variants.

Session 4: payload import and data availability

Read process_execution_payload in beacon-chain.md and the envelope and data-column gossip conditions in p2p-interface.md. Then read the envelope verifier, PayloadEnvelopeInput, payload import, and Gloas data-column validation.

Goal: separate availability, CL consistency, BLS validity, and EL validity into four distinct checks.

Session 5: honest validator and builder flows

Read the pinned validator.md and builder.md, then the Lodestar block, PTC, proposer-preferences, bid-validation, and block-production services.

Goal: trace both a self-build and an external-builder build from API request to gossip publication.

Session 6: tests and failure cases

Run targeted unit and spec tests. Focus on withheld payloads, absent versus negative PTC votes, fork transition, builder overcommitment, invalid request roots, and empty-parent withdrawals.

Goal: for each rejected object, name whether it is rejected during gossip, state transition, envelope import, EL verification, or fork choice.

Hands-on commands

From the repository root:

# Builder helper behavior
pnpm vitest run --project unit packages/state-transition/test/unit/util/gloas.test.ts

# Builder registration and exit requests
pnpm vitest run --project unit packages/state-transition/test/unit/block/processBuilderDepositRequest.test.ts
pnpm vitest run --project unit packages/state-transition/test/unit/block/processBuilderExitRequest.test.ts

# Focused fork-choice examples
pnpm vitest run --project unit packages/fork-choice/test/unit/protoArray/gloas.test.ts \
  -t "creates FULL variant when payload arrives"

pnpm vitest run --project unit packages/fork-choice/test/unit/protoArray/gloas.test.ts \
  -t "shouldBuildOnFull"

Run the Gloas minimal-preset operation tests from the beacon-node package:

cd packages/beacon-node
pnpm vitest run --project spec-minimal test/spec/presets/operations.test.ts

The downloaded fixtures are organized by concept under packages/beacon-node/spec-tests/tests/minimal/gloas/, including operations/execution_payload_bid, operations/parent_execution_payload, operations/payload_attestation, epoch_processing/builder_pending_payments, and several fork-choice suites.

Exercises

Exercise 1: container diff

Write down every field removed from Fulu's BeaconBlockBody and every Gloas replacement. For each removed field, identify where its data travels after Gloas.

Expected insight: the data was not eliminated; it moved off the beacon block's initial propagation path.

Exercise 2: full versus empty parent

Start with:

For the child bid, evaluate these cases:

  1. parentBlockHash = H1
  2. parentBlockHash = H0

Then list which case applies parent execution requests and which execution hash the new payload extends.

Answer: case 1 selects the full parent and applies its requests; case 2 selects the empty parent and continues from H0.

Exercise 3: PTC tri-state votes

For a four-member toy committee, classify each member as “yes,” “no,” or “did not attest.” Explain why representing only a payloadPresent bitvector loses information.

Expected insight: a zero bit cannot distinguish an explicit negative vote from absence. Lodestar tracks attendance separately.

Exercise 4: payment without payload

Trace a selected bid whose beacon block is well-attested but whose payload is withheld. Find where attestation weight is added, where the epoch threshold is checked, and where the builder balance is finally reduced.

Expected insight: beacon-block support and payload timeliness are deliberately different signals.

Exercise 5: classify validation

For each failure, locate the Lodestar layer that should catch it:

There may be defensive checks at more than one layer. Identify the earliest normal rejection point and any repeated state-transition check.

Common misconceptions

“Empty means no beacon block”

No. EMPTY means the beacon block exists but its promised execution payload is not part of that execution history. A skipped slot has no beacon block.

“PTC votes make the payload execution-valid”

No. The PTC reports timely presence and blob-data availability. The execution engine determines execution validity.

“Attestation index 0 always means empty”

No. For same-slot Gloas attestations, index 0 supports PENDING. It maps to EMPTY only after the attestation can distinguish payload status.

“The envelope updates beacon state when it arrives”

No. Lodestar verifies, stores, and adds a full fork-choice variant. A child beacon block later makes the parent payload's state effects canonical.

“PTC weight pays the proposer”

No. Same-slot ordinary beacon-attestation weight contributes to builder payment support. PTC observations serve the payload-choice rules.

“A Gloas builder is another validator”

No. The builder has stake and a BLS key but no validator duties or validator rewards. The registries and balances are separate.

“All current Lodestar Gloas code is production-complete”

No. Search for TODO GLOAS, inspect skipped spec suites, and check the latest unstable branch. Use this snapshot to learn the design, not as evidence that Gloas is scheduled or complete.

Check your understanding

Try to answer these without looking back:

  1. Why can the same beacon block root have both full and empty variants?
  2. What field in the child bid identifies the chosen parent execution variant?
  3. Why are parent execution requests repeated in the child beacon block?
  4. What is the difference between payloadPresent and EL validity?
  5. Why does a same-slot ordinary attestation use index 0 without voting empty?
  6. Which votes add builder-payment weight?
  7. What happens to withdrawals when the parent is empty?
  8. At what point does a verified envelope affect canonical beacon state?
  9. Why does process_execution_payload still exist in Gloas even though process_block no longer calls it?
  10. A bid arrives with a builder balance that cannot cover it. Which spec document rejects it first, and which spec function rejects it again if it reaches a block?

If you can answer all ten and trace both the spec functions and the Lodestar implementations, you have the core Gloas model—and you are ready to read the rest of the spec documents end to end.

Primary references

Use the pinned versions while following this guide: