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:
- Why Gloas separates a beacon block from its execution payload.
- What proposers, builders, ordinary attesters, and the Payload Timeliness Committee (PTC) do during a slot.
- Why one beacon block root can have
PENDING,EMPTY, andFULLfork-choice variants. - Why a child beacon block applies its parent's execution-payload effects.
- How in-protocol builder bids and payments work at a high level.
- How the five Gloas spec documents divide the protocol, and which spec functions carry the design.
- 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:
- The consensus layer (CL) agrees on beacon blocks and validator votes.
- The execution layer (EL) executes transactions and produces execution payloads.
- A slot has one selected beacon proposer and committees of attesters.
- Fork choice uses validator attestations to select the head.
- Before Gloas, a post-Merge beacon block directly carries an execution payload, or a blinded commitment that is unblinded before the complete block is propagated.
- Builders already exist in the external MEV supply chain, but the pre-Gloas consensus protocol does not maintain a builder registry or enforce their payments.
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:
- EIP-7843: a slot number in the execution payload for the
SLOTNUMopcode. - EIP-7928: block-level access lists carried by the execution payload.
- EIP-8045: exclusion of slashed validators from proposer selection.
- EIP-8061: exit and consolidation churn changes.
- EIP-8282: execution-layer requests for builder deposits and exits.
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:
- ePBS is the proposer-builder separation mechanism introduced by EIP-7732.
- Gloas is the consensus fork that contains ePBS plus the other EIPs listed above.
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:
| Document | Question it answers |
|---|---|
beacon-chain.md | What are the containers, constants, and the deterministic state transition? |
fork-choice.md | How do received blocks, envelopes, and PTC messages change the head? |
p2p-interface.md | Which gossip topics and req/resp methods exist, and when is a message invalid? |
validator.md | What should an honest proposer, attester, and PTC member do, and when? |
builder.md | What should an honest builder do with bids and envelopes? |
Two conventions make the spec much faster to read once you know them:
- 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. - Numbers live outside the documents. Preset values such as
PTC_SIZEare defined per preset (presets/mainnet,presets/minimal), and per-network values such asGLOAS_FORK_EPOCHlive underconfigs/. Lodestar mirrors both inpackages/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 Gloas | In Gloas |
|---|---|
BeaconBlockBody.executionPayload | BeaconBlockBody.signedExecutionPayloadBid |
| Blob commitments in the beacon block | Blob commitments in the bid; payload data travels separately |
| Execution requests in the beacon block | Requests in the envelope and repeated as parentExecutionRequests by the child |
| One block root represents the block and embedded payload | One beacon block root can have pending, empty, and full payload views |
| Builders and payments are external to consensus | Staked 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:
- Publish a
SignedExecutionPayloadBidcommitting to an execution block and a payment. - If selected, reveal the matching
SignedExecutionPayloadEnvelopeand 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:
0meansEMPTYwhen the payload status can be decided.1meansFULL.- A same-slot attestation must use
0, but fork choice interprets this as a vote forPENDING, not as a premature vote forEMPTY.
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:
payloadPresent: was the matching envelope seen on time?blobDataAvailable: was the corresponding blob data available?
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:
- resolves and applies the slot
N-1parent payload, if full; - processes withdrawals and records those expected in the slot
Npayload if the parent is full, or pauses withdrawal processing if the parent is empty; - reserves the selected builder's payment; and
- creates
PENDINGandEMPTYfork-choice variants for the slotNbeacon 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:
- PTC members observe whether the envelope was timely and whether its blob data was available, then publish their observations near 75% of the slot.
- Beacon nodes wait for required data, verify the envelope and bid commitments, check the BLS signature, and ask the execution engine to validate the payload.
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:
- To choose
FULL, its new bid extends payloadN'sblockHash, and its beacon block repeats payloadN's execution requests asparentExecutionRequests. - To choose
EMPTY, its new bid extends payloadN'sparentBlockHash, andparentExecutionRequestsmust be empty.
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 result | What block N+1 does |
|---|---|
FULL | Applies payload N's execution requests, marks it available, advances latestBlockHash, and settles its builder payment |
EMPTY | Applies 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 point | Approximate time | Activity |
|---|---|---|
| Start | 0 seconds | Proposer publishes the beacon block containing a bid |
| 25% | 3 seconds | Ordinary attestations and sync messages are due |
| 50% | 6 seconds | Attestation aggregates and sync contributions are due |
| 75% | 9 seconds | Payload and PTC payload attestations are due |
| Next slot | 12 seconds | The 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 N | Local fork-choice result | What the slot N+1 child applies |
|---|---|---|
| Timely envelope, data available, payload accepted, no negative PTC majority | FULL can be selected | Payload requests, execution hash, and direct payment settlement |
| Builder withholds the envelope | Only PENDING/EMPTY are usable | No payload effects; payment may still be earned from ordinary attestation support |
| Envelope or commitment is invalid | Envelope is rejected; no valid FULL variant from it | Empty-parent path |
| EL reports the payload invalid | Full execution branch is invalidated | Empty-parent or another viable chain |
| Envelope is known but strict-majority PTC evidence says late or unavailable | shouldBuildOnFull chooses EMPTY for the recent parent | No 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:
- proposal slot;
- fee recipient;
- target gas limit;
- proposer index; and
- dependent root, which binds the preferences to the relevant chain history.
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:
| Field | Meaning |
|---|---|
parentBlockRoot | Parent beacon block root |
parentBlockHash | Execution block hash the new payload extends |
blockHash | Promised hash of the new execution payload |
builderIndex | Registered builder, or the self-build sentinel |
slot | Slot for the proposed payload |
value | Builder's promised in-protocol payment in Gwei |
blobKzgCommitments | Commitment to the payload's blob data |
executionRequestsRoot | Commitment 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:
signedExecutionPayloadBidfor the current slot;payloadAttestations, aggregating PTC messages about the parent slot; andparentExecutionRequests, carrying requests from the parent envelope so the parent payload's effects can be applied deterministically.
4. SignedExecutionPayloadEnvelope
The envelope reveals:
- the complete execution payload;
- execution requests;
- the builder index;
- the beacon block root that selected the bid; and
- that beacon block's parent root.
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]:
| Identifier | Points to |
|---|---|
B[N].parentRoot | Parent beacon block B[N-1] |
bid.parentBlockRoot | Also B[N-1] |
bid.parentBlockHash | Execution parent chosen for E[N] |
bid.blockHash | The promised E[N] |
envelope.beaconBlockRoot | The beacon block B[N] that selected the bid |
envelope.parentBeaconBlockRoot | B[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
latestBlockHash: latest execution block hash actually applied by the canonical beacon-state transition.latestExecutionPayloadBid: most recently processed bid.executionPayloadAvailability: historical bitvector recording which canonical slots had an applied payload.payloadExpectedWithdrawals: withdrawals the separately arriving payload is required to contain.
Builder accounting
builders: registry of builder pubkeys, execution addresses, balances, and lifecycle epochs.builderPendingPayments: two epochs of payment obligations and supporting attestation weight.builderPendingWithdrawals: obligations ready to be emitted as execution withdrawals.nextWithdrawalBuilderIndex: cursor for sweeping exited builder balances.
PTC lookahead
ptcWindow: committees for the previous, current, and lookahead epochs.
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:
- If the current bid's
parentBlockHashequals the previous bid'sblockHash, the child extends the parent'sFULLvariant. - Otherwise, it extends the parent's
EMPTYvariant.
For a full parent, processParentExecutionPayload verifies that parentExecutionRequests hashes to the root committed by the parent bid. It then:
- applies deposits, withdrawals, consolidations, builder deposits, and builder exits from the parent payload;
- settles the parent builder payment;
- marks the parent payload available; and
- advances
latestBlockHash.
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:
- Waits for the required data columns.
- Finds the corresponding beacon block in fork choice.
- Regenerates the block's post-state.
- Verifies the envelope against that state and its bid.
- Verifies the BLS signature and asks the EL to verify the payload.
- Persists the envelope.
- Adds a
FULLfork-choice variant. - 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:
PENDING: beacon block known, payload outcome not decided.EMPTY: beacon block remains, but its promised execution payload is not on this execution chain.FULL: matching payload envelope exists and is verified or optimistically accepted.
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:
onBlock(specon_block) createsPENDINGandEMPTY.onExecutionPayload(specon_execution_payload_envelope) addsFULLas a sibling ofEMPTY.- Same-slot ordinary attestations vote for
PENDING. - A later ordinary attestation uses index
0or1to add LMD weight toEMPTYorFULL. - PTC messages arrive through
on_payload_attestation_message(andnotify_ptc_messagesfor 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. - A child bid's
parentBlockHashdeclares which parent variant its execution payload extends; the spec'sis_parent_node_fullpredicate 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:
- Reserved: processing the bid records a
BuilderPendingPayment.canBuilderCoverBidprevents the builder from promising the same balance repeatedly. - Earned: same-slot ordinary beacon attestations add balance weight to the payment. This is ordinary attestation weight, not PTC weight.
- Withdrawable: extending the parent's full payload settles the payment directly. Otherwise, epoch processing (
process_builder_pending_payments, using theget_builder_payment_quorum_thresholdsupport threshold) can accept a sufficiently supported payment and move it tobuilderPendingWithdrawals.
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.
- The slot
Nbeacon block is published with builder 42's signed bid. - Beacon nodes create
PENDINGandEMPTYvariants for its root. - Same-slot attesters support
PENDINGand add weight to the payment. - Builder 42 publishes a matching envelope and data before the deadline.
- PTC members report
payloadPresent = trueandblobDataAvailable = trueaccording to their views. - In parallel, nodes verify the commitments, signature, data availability, and EL result.
- Fork choice gains a
FULLvariant after successful payload import. - The slot
N+1proposer builds on the full execution hash, includes the parent's execution requests, and aggregates the PTC messages. - While processing block
N+1, Lodestar applies slotN's requests, settles the builder payment, marks the payload available, and advanceslatestBlockHash.
Example B: builder withholds the payload
- The slot
Nbeacon block is still valid and receives ordinary attestations. - No matching envelope arrives on time, so no
FULLvariant is available. - PTC members report
payloadPresent = falseaccording to their views. - The next proposer builds on the
EMPTYvariant. Its bid extends the earlier execution hash, not the withheld payload's promised hash. parentExecutionRequestsis empty, so the withheld payload's transactions and requests have no canonical effect.- 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
| Property | Ordinary beacon attestation | PTC payload attestation |
|---|---|---|
| Main purpose | Vote for consensus head and checkpoints | Report payload timeliness and blob-data availability |
| Committee | Beacon attestation committee | Payload Timeliness Committee |
| Main payload signal | AttestationData.index selects pending/empty/full semantics | payloadPresent and blobDataAvailable booleans |
| Fork-choice effect | Adds LMD weight | Influences recent full/empty decision rules |
| Builder-payment effect | Same-slot support adds payment weight | No direct payment weight |
| Deadline in this snapshot | 25% of slot | 75% of slot |
| Requires full EL execution first? | No, same-slot vote is pending | No |
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.
| Step | Topic | Lodestar entry point |
|---|---|---|
| 1 | Fork name and constants | forkName.ts, params/index.ts |
| 2 | SSZ messages and state | gloas/sszTypes.ts |
| 3 | Fork upgrade | upgradeStateToGloas.ts |
| 4 | Block-processing order | state-transition/src/block/index.ts |
| 5 | Parent payload effects | processParentExecutionPayload.ts |
| 6 | Bid and PTC operations | processExecutionPayloadBid.ts, processPayloadAttestation.ts |
| 7 | Builder helpers and accounting | util/gloas.ts, processBuilderPendingPayments.ts |
| 8 | Envelope verification and import | verifyExecutionPayloadEnvelope.ts, importExecutionPayload.ts |
| 9 | Full/empty fork choice | protoArray.ts |
| 10 | Block production | produceBlockBody.ts |
| 11 | Validator duties | validator/services/block.ts, ptc.ts, proposerPreferences.ts |
| 12 | Gossip and req/resp | network/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
| Function | Role |
|---|---|
process_parent_execution_payload | Applies the parent payload's effects inside the child block |
process_withdrawals | Computes expected withdrawals; pauses them on an empty parent |
process_execution_payload_bid | Validates the selected bid and reserves the builder payment |
process_payload_attestation | Processes the PTC aggregates included in a block |
process_execution_payload | Verifies the revealed envelope against the block's post-state |
process_builder_deposit_request, process_builder_exit_request | Builder registry entry and exit |
process_builder_pending_payments | Epoch step that promotes supported payments to withdrawals |
get_ptc, is_valid_indexed_payload_attestation | PTC selection and payload-attestation signature checking |
can_builder_cover_bid, get_builder_payment_quorum_threshold | Builder balance-coverage and payment-quorum rules |
In fork-choice.md
| Function | Role |
|---|---|
on_block | Adds a block; in Gloas, seeds the pending and empty variants |
on_execution_payload_envelope | Adds the full variant when an envelope is imported |
on_payload_attestation_message, notify_ptc_messages | Record PTC observations in the store |
should_build_on_full, should_extend_payload | Decide whether the next proposal extends the full parent |
is_parent_node_full, is_payload_verified | Predicates connecting bids to parent variants |
get_head | Head 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:
- the exact spec function and hash;
- the Lodestar source file; and
- the search string identifying the implementation.
Use this repeatable study loop:
- Read one function in the pinned consensus spec.
- Find its entry in
specrefs/functions.ymlor its container inspecrefs/containers.yml. - Read the mapped TypeScript function.
- Search for its callers with
rg. - 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.md—on_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:
latestBlockHash = H0;- previous bid
parentBlockHash = H0; and - previous bid
blockHash = H1.
For the child bid, evaluate these cases:
parentBlockHash = H1parentBlockHash = 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:
- unknown parent beacon root;
- builder balance too low;
- envelope request root differs from bid;
- missing sampled data columns;
- EL reports an invalid payload; and
- attestation uses index
1before the payload is known.
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:
- Why can the same beacon block root have both full and empty variants?
- What field in the child bid identifies the chosen parent execution variant?
- Why are parent execution requests repeated in the child beacon block?
- What is the difference between
payloadPresentand EL validity? - Why does a same-slot ordinary attestation use index 0 without voting empty?
- Which votes add builder-payment weight?
- What happens to withdrawals when the parent is empty?
- At what point does a verified envelope affect canonical beacon state?
- Why does
process_execution_payloadstill exist in Gloas even thoughprocess_blockno longer calls it? - 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:
- Gloas beacon chain
- Gloas fork choice
- Gloas honest validator
- Gloas honest builder
- Gloas networking
- Spec test formats, which document the vectors under
packages/beacon-node/spec-tests/ - EIP-7732
- Lodestar specification references