ePBS (EIP-7732) Impact on Rocket Pool¶
Overview¶
Enshrined Proposer-Builder Separation (ePBS), specified under EIP-7732 and the "Gloas" consensus fork, moves the proposer-builder relationship from an out-of-protocol sidecar (MEV-Boost) into the consensus layer itself. This document summarises how ePBS works and analyses its ramifications for Rocket Pool.
How ePBS Works¶
Current State (MEV-Boost)¶
Today, proposer-builder separation operates via MEV-Boost — an out-of-protocol relay system:
- Builders construct execution payloads and submit them to relays
- Relays hold the payloads and expose bid headers to proposers
- Proposers select a header and sign a blinded block
- The relay reveals the full payload once the signed header is committed
The proposer sets their fee_recipient in the execution payload via their validator client configuration. There is no on-chain enforcement of what that address should be.
ePBS (Gloas Fork)¶
ePBS enshrines this relationship in the consensus protocol with three new actors/mechanisms:
Builders as Protocol Participants¶
Builders become first-class staked participants on the beacon chain:
class Builder(Container):
pubkey: BLSPubkey
version: uint8
execution_address: ExecutionAddress
balance: Gwei
deposit_epoch: Epoch
withdrawable_epoch: Epoch
- Builders register by depositing ETH with a
BUILDER_WITHDRAWAL_PREFIX(0x03) credential - They are assigned a
builder_indexand become active after their deposit epoch is finalised - Builders do not perform validator duties (no attesting, no proposing) and do not earn yield on their stake
Proposer Preferences (Gossip)¶
Proposers broadcast their preferences via a P2P gossip message:
class ProposerPreferences(Container):
proposal_slot: Slot
validator_index: ValidatorIndex
fee_recipient: ExecutionAddress
gas_limit: uint64
- Broadcast at the beginning of each epoch for upcoming proposal slots
- Signed by the validator (
SignedProposerPreferences) - Slot-scoped — each message targets a specific slot
- If a validator does not broadcast preferences, they signal they will not accept trustless bids for that slot (i.e. they intend to self-build)
- Not stored in beacon state — ephemeral gossip-layer messages only
Execution Payload Bids (On-Chain)¶
Builders submit bids, and the proposer commits one into their beacon block:
class ExecutionPayloadBid(Container):
parent_block_hash: Hash32
parent_block_root: Root
block_hash: Hash32
prev_randao: Bytes32
fee_recipient: ExecutionAddress
gas_limit: uint64
builder_index: BuilderIndex
slot: Slot
value: Gwei
execution_payment: Gwei
blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
- The proposer includes a
SignedExecutionPayloadBidinBeaconBlockBody - For external builders:
bid.fee_recipientmust match the proposer'sSignedProposerPreferences(enforced at P2P gossip validation) - For self-builds:
builder_index = BUILDER_INDEX_SELF_BUILD(UINT64_MAX),value = 0, signature is infinity point
Payment Flow¶
When a builder bid with value > 0 is committed:
- A
BuilderPendingPaymentis recorded in beacon state atprocess_execution_payload_bid - The payment includes a
BuilderPendingWithdrawalwith thefee_recipientfrom the bid - At epoch processing, pending payments are converted to
Withdrawalobjects - The withdrawal delivers ETH to
fee_recipientvia the execution layer
class BuilderPendingWithdrawal(Container):
fee_recipient: ExecutionAddress
amount: Gwei
builder_index: BuilderIndex
Payload Timeliness Committee (PTC)¶
A subset of validators attests to whether the execution payload was delivered on time and whether blob data is available. This replaces the relay's role as an honest intermediary.
Self-Build Path¶
When a proposer does not broadcast SignedProposerPreferences:
- No builders can construct valid bids (P2P layer rejects bids without matching preferences)
- The proposer builds their own execution payload via
prepare_execution_payload - They set
suggested_fee_recipientdirectly inPayloadAttributes - This becomes the coinbase in the execution payload
- The
ExecutionPayloadBidis still committed in the beacon block, but withvalue = 0andbuilder_index = BUILDER_INDEX_SELF_BUILD bid.fee_recipientis effectively meaningless in this case — the actual fee recipient is the execution payload coinbase
Ramifications for Rocket Pool¶
Fee Recipient Enforcement¶
Rocket Pool node operators are expected to set their fee recipient to: - Their megapool address (if megapool validator) - Their distributor address (if minipool validator) - The smoothing pool address (if opted in)
A node operator who sets an incorrect fee recipient bypasses the protocol and diverts rETH holders' share of rewards.
The fundamental problem is unchanged with ePBS — the node operator still controls what fee_recipient they broadcast or set. Nothing at the consensus layer forces a specific address.
Detection Improvements¶
ePBS improves detection compared to today's MEV-Boost setup:
| Aspect | Today (MEV-Boost) | ePBS |
|---|---|---|
| Fee recipient evidence | Execution payload coinbase (EL blocks) | bid.fee_recipient in beacon block (CL) + execution payload coinbase (EL) |
| Attribution | Requires correlating proposer schedule with EL blocks | Slot-scoped: validator index known per slot, bid committed in their beacon block |
| Cryptographic proof | None — coinbase is set by whoever builds the block | SignedProposerPreferences are signed by the validator (gossip only, not on-chain) |
| Builder vs proposer distinction | Ambiguous — hard to distinguish relay/builder behaviour from proposer intent | Clear: external bids have builder_index, self-builds have BUILDER_INDEX_SELF_BUILD |
Two Detection Paths Required¶
Because the fee recipient surfaces differently depending on the block construction method, detection requires two approaches:
1. External Builder Blocks¶
bid.fee_recipientis committed on-chain in theExecutionPayloadBidwithin the beacon block- The P2P layer enforces
bid.fee_recipientmatchesSignedProposerPreferences, so the committed bid reflects the proposer's declared preference - Detection: Compare
bid.fee_recipientagainst the expected protocol address for that validator - Data source: Beacon chain (CL data)
2. Self-Built Blocks¶
bid.value = 0andbid.fee_recipientis meaningless- The actual fee recipient is the
coinbasein the execution payload - Detection: Compare execution payload coinbase against the expected protocol address
- Data source: Execution layer (same as today)
- Can be identified by
bid.builder_index == BUILDER_INDEX_SELF_BUILD
Implementation Considerations¶
Oracle/Monitoring Architecture¶
The block processing approach (reading committed beacon blocks) fits naturally into Rocket Pool's existing architecture: - The oDAO already processes beacon chain data for network balance reporting - Adding a fee recipient compliance check to this pipeline is incremental - No new gossip monitoring infrastructure required
The gossip monitoring approach (subscribing to proposer_preferences topic) would only add value for pre-emptive alerting — it cannot prevent violations, only detect intent before the block. Since the gossip messages are ephemeral and not stored in beacon state, this approach requires additional infrastructure and has no advantage for penalty enforcement (on-chain evidence from the committed bid/coinbase is sufficient).
Recommendation: Use the block processing approach. Detect fee recipient non-compliance from committed beacon blocks and execution payloads within existing oDAO infrastructure.
Relationship to RPIP-73¶
RPIP-73 (rETH Protection From Underperforming Nodes) currently specifies detection for: 1. Offline validators (attestation performance) 2. Poor performing validators (attestation performance)
Fee recipient non-compliance is a natural third detection category. The same oracle infrastructure that monitors attestation performance can also check fee recipients per slot. The penalty and forced exit mechanism from RPIP-44 applies equally.
Key differences from attestation-based detection: - Fee recipient diversion is intentional — a lower tolerance threshold is appropriate - The evidence is per-proposal (not per-attestation), so occurrences are less frequent but each instance has higher impact - A feature flag (pDAO parameter) could gate this detection category until the ePBS fork activates
Quantifying Diverted Value¶
ePBS makes it possible to not only detect fee recipient non-compliance but also quantify the value diverted, though the completeness depends on the block construction method:
External builder blocks (fully quantifiable from CL data):
- bid.value (in Gwei) records the payment the builder made to the proposer
- bid.fee_recipient records where that payment went
- If fee_recipient doesn't match the expected protocol address, bid.value is the amount diverted
- No EL data needed — the beacon block contains everything required
Self-built blocks (requires EL data):
- bid.value = 0 by definition, so the CL data contains no value information
- The actual value captured (transaction priority fees + MEV) is only determinable from the execution payload on the EL
- However, self-built blocks are identifiable from CL data alone via bid.builder_index == BUILDER_INDEX_SELF_BUILD
- This narrows the scope: only self-built blocks require EL inspection to quantify diversion
Practical implication: Since the majority of blocks use external builders, most diversion is fully quantifiable from beacon chain data alone. The oDAO or any monitoring system can produce accurate diversion reports without needing to cross-reference EL blocks for the common case. Self-build blocks represent a smaller subset that requires additional EL analysis but can be flagged and queued for inspection efficiently.
Self-Build as a Signal¶
A Rocket Pool validator consistently using self-build (BUILDER_INDEX_SELF_BUILD) when the builder market is active could indicate fee diversion — though there are legitimate reasons (local building preference, MEV ethical concerns). This should be monitored but not automatically penalised.
Implementation Assessment¶
Existing Infrastructure¶
The smartnode watchtower already implements fee recipient penalty detection in process-penalties.go. The current system:
- Scans beacon blocks for proposals by Rocket Pool validators (from a checkpoint, default ~8 weeks back)
- Resolves the expected fee recipient per validator (smoothing pool, fee distributor, or megapool address) via
FeeRecipientInfo - Compares the actual fee recipient in the execution payload against the expected address
- Detects three violation types: smoothing pool theft, opt-out evasion, and distributor theft
- Submits penalties on-chain via
SubmitPenalty(minipoolAddress, slot) - Tracks progress via
LatestPenaltySlotin a YAML checkpoint file
The address resolution logic (shared/utils/rp/fee-recipient.go) already handles the full decision tree:
- Smoothing pool → smoothing pool address
- Minipools only → fee distributor address
- Megapool validators only → megapool address
- Mixed (megapool + minipool) → per-key overrides via keymanager API
The beacon client (shared/services/beacon/client.go) provides GetBeaconBlock(blockId) with full execution payload parsing, attestation data, and committee information.
What Changes for ePBS¶
The existing detection loop in process-penalties.go currently reads the execution payload coinbase. For ePBS:
- External builder blocks: Read
bid.fee_recipientfromblock.body.signed_execution_payload_bid.messageinstead of the execution payload coinbase - Self-build blocks: Check
bid.builder_index == BUILDER_INDEX_SELF_BUILD, then fall back to execution payload coinbase (same as current behaviour) - Value tracking: For builder blocks, record
bid.valueas the diverted amount alongside the penalty submission
Effort Estimate¶
Low-medium effort. The detection logic, penalty submission, address resolution, and watchtower scheduling are all in place. The changes are incremental:
| Change | Effort | Notes |
|---|---|---|
| Update beacon client types for Gloas fork block structure | Medium | New ExecutionPayloadBid, SignedExecutionPayloadBid structs in beacon client; JSON/SSZ deserialisation |
| Branch detection logic by builder vs self-build | Small | Simple conditional in existing scan loop in process-penalties.go |
Read bid.fee_recipient instead of coinbase for builder blocks |
Trivial | Field swap in existing comparison |
Record bid.value for diversion quantification |
Small | New field in penalty reporting / logging |
| Feature flag for ePBS activation | Small | pDAO parameter check before using new code path |
The biggest piece of work is updating the beacon client types (shared/services/beacon/) to deserialise the Gloas fork's BeaconBlockBody, which includes the new SignedExecutionPayloadBid container. Everything downstream — the detection logic, address resolution, and penalty submission — already exists and requires only minor modifications.
Rough estimate: 1-2 weeks of development, primarily beacon client type updates and testing against a Gloas devnet. The detection logic changes are approximately 1-2 days since the pattern is already established.
Key Files¶
| Component | Path |
|---|---|
| Penalty detection (main change) | smartnode/rocketpool/watchtower/process-penalties.go |
| Fee recipient resolution | smartnode/shared/utils/rp/fee-recipient.go |
| Fee recipient management | smartnode/shared/services/rocketpool/fee-recipient.go |
| Beacon client interface | smartnode/shared/services/beacon/client.go |
| Beacon client HTTP impl | smartnode/shared/services/beacon/client/std-http-client.go |
| Network state | smartnode/shared/services/state/network-state.go |
| Watchtower orchestration | smartnode/rocketpool/watchtower/watchtower.go |
Open Questions¶
- Should fee recipient non-compliance be a single-violation penalty or follow a threshold pattern like RPIP-73's attestation checks? Fee diversion is more clearly intentional than poor attestation performance, which suggests lower tolerance. However, misconfiguration is possible, especially during setup.
- What penalty amount is appropriate? Unlike attestation underperformance where penalty calibration relates to lost rewards over time, fee diversion penalty should arguably relate to the value diverted (which varies per block).
- How should the transition period work? If ePBS activates after Saturn 2 ships, the fee recipient detection for external builder blocks would need to be feature-flagged.
- Should there be a grace period for self-built blocks? Legitimate reasons for self-building exist, but it does bypass the standard fee recipient declaration path.
- Can the existing
SubmitPenaltycontract call accommodate the new diversion value data? Or does the penalty contract need updating to accept a value parameter for quantified diversion reporting?
Based on the Gloas consensus spec (EIP-7732) at ethereum/consensus-specs and the smartnode-http codebase.