
TLDR ⚡
- [Project Proposal](https://github.com/eth-protocol-fellows/cohort-six/blob/master/projects/lighthouse_epbs.md)
- [Shipped Work](#Shipped-Work-🚢) · [Path To Glamsterdam](#Path-To-Glamsterdam-🚀) · [Cohort Reflections](#Cohort-Reflections-🪞) · [Takeaways](#Takeaways-🔑) · [Fellowship Thanks](#Fellowship-Thanks-🙏)
- [My Lighthouse PR's](https://github.com/sigp/lighthouse/pulls?q=is%3Apr+author%3Ashane-moore+) (more to come 😉)
## Abstract 🎯
I joined the [Ethereum Protocol Fellowship](https://blog.ethereum.org/2025/04/10/epf-6) to do applied research that moves the roadmap forward, with a focus on scaling the L1. I was fortunate to work on ePBS, [EIP-7732](https://eips.ethereum.org/EIPS/eip-7732), inside [Lighthouse](https://github.com/sigp/lighthouse), a Rust-based Ethereum consensus client. I moved Lighthouse toward a realistic devnet-0 implementation while staying aligned with the ever-evolving [consensus specs](https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/) and documented the path in my [project proposal](https://github.com/eth-protocol-fellows/cohort-six/blob/master/projects/lighthouse_epbs.md).
It was a fascinating feature to work on since it touches almost every aspect of the consensus layer, such as block proposal, block processing, payload processing, beacon api, p2p gossiping, and a new slot-based voting committee.
**ePBS in one line:** decouple execution payloads from consensus blocks so payload validation can take ~9s instead of ~100ms today, which enables significant gas limit increases and will ship as a Glamsterdam headliner 🎉
<details close>
<summary><strong>ePBS in more lines</strong></summary>
The validator client is the driver of the consensus layer. It coordinates the block proposal and block validation processes by requesting blocks from the beacon node to propose and additionally signs attestations during block verification. Since the execution payload will be seperated from the CL block in the ePBS world, the VC will now need to be able to request execution payloads from the beacon node to sign as well. Additionally, we'll need to introduce a new Payload Timeliness Committee (PTC) where the validator client will request attestations to sign from the beacon node to ensure the payload was released during the same slot as the block.
The validator client communicates with the beacon node via the beacon api, so the endpoints for requesting payload and payload attestations to sign had to be defined. So I made a [PR](https://github.com/ethereum/beacon-APIs/pull/552) to the beacon api spec to implement this!
The beacon node is the engine of the consensus layer. It does everything from producing blocks to handling state transitions. During block processing with ePBS, the beacon node will no longer be verifying an execution payload as part of the block but instead an execution bid. Bids were submitted by in-protocol builders, and the winning bid gets included in the block by the proposer.
The beacon node is part of a distributed p2p network. Today, it receives blocks and block validity attestations from its peers over libp2p. It will now also be responsible for verifying payloads and payload attestations that arrive via peers. To support this, the beacon node will subscribe to new gossipsub topics as to obtain payloads and payload attestations immediately when available.
</details>
ePBS is a massive EIP, so I scoped to shippable pieces that delivered value and kept the PRs idiomatic to Lighthouse. Overview of my work:
- **Core data structures and state transitions:** added ePBS containers (`SignedExecutionPayloadEnvelope`), extended `BeaconBlockBody` and `BeaconState`, implemented `process_payload_attestations` and `process_execution_payload_bid` per the [spec](https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#block-processing), and introduced a per-epoch PTC cache for O(1) lookups
- **Validator Client and PTC:** request, sign, and publish execution payload envelopes; v4 block production paths; Web3Signer support; a 3/4th of slot payload attestation service that batches a single POST
- **Beacon API:** defined v4 `/validator/blocks/{slot}`, clean GET/POST pairs for `ExecutionPayloadBid` and `ExecutionPayloadEnvelope`, plus PTC duties and payload attestation endpoints
- **Networking:** new gossipsub topics `execution_payload` and `payload_attestation_message`; SSZ-Snappy RPCs to fetch envelopes by range and by root with explicit queues and limits
## Shipped Work 🚢
### Quick Summary
| PRs | Amount |
|-------|--------|
| Merged| 10 |
| Open | 6 |
| Local | 7 |
| Total | 23 |
#### Phase 1: Core Data Structures
I kicked off by creating the ePBS containers, such as `SignedExecutionPayloadEnvelope`, with canonical SSZ and spec-aligned typing. I extended `BeaconBlockBody` and `BeaconState` with the Gloas fields while preserving backwards compatibility for deprecated members. The work started in [PR #7807](https://github.com/sigp/lighthouse/pull/7807) and was rebased onto `unstable` in [PR #7923](https://github.com/sigp/lighthouse/pull/7807), followed by a myriad of test fix PRs to keep CI green. In the end, lighthouse has a feature complete container set that unblocks downstream efforts. This was a solid crash course in how lighthouse maps consensus spec containers to Rust types. Not glamorous, but exactly the foundation we needed to hit the ground running.
#### Phase 2: State Transitions
The next step was to update the epoch processing, slot processing, and block processing functions per the [consensus spec](https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#beacon-chain-state-transition-function).
**PR**: [add process_payload_attestation](https://github.com/sigp/lighthouse/pull/8286)
I hooked payload attestations into block processing for Gloas by adding `process_payload_attestations` to the per-block pipeline with proper verifications plus all the usual cache warm-ups such as the `committee_cache` so that we don't pay state lookup costs during validation.
In order to validate the payload attestations proposers intend to include in a block, I introduce `get_indexed_payload_attestation` and `is_valid_indexed_payload_attestation`, which map a PTC bitvector to validator indices from the previous slot, allow duplicates while enforcing sorted order, and verify the aggregate signature via the new signature set, so we can cheaply answer the question of "who attested".
**PR**: [PTC Cache](https://github.com/shane-moore/lighthouse/pull/10)
This PR introduces a PTC cache that precomputes and stores the payload-timeliness committee (PTC) shuffling per epoch, plus O(1) position lookups, so we pay the heavy selection cost once at epoch boundary instead of every time we call `get_ptc` on the hot path during proposal and validation.
The cache is a flat `ptc_shuffling: Vec<usize>` laid out as `[slot1…slot32]` chunks of length `ptc_size (512)`, so locating committee for slot S is just a slice. For inverse lookups, `ptc_slot_assignments: HashMap<usize, u8>` maps a validator index to its committee slot in the epoch, so we can do a single read to determine if the validator is in a committee for a slot.
Other notable PR's:
- [Gloas modify process_withdrawals](https://github.com/sigp/lighthouse/pull/8281)
- [Gloas modify process_attestations](https://github.com/sigp/lighthouse/pull/8283)
- [Gloas modify process epoch and process slot](https://github.com/sigp/lighthouse/pull/8287)
- [Gloas add process_execution_payload_bid](https://github.com/sigp/lighthouse/pull/8355)
- [Gloas modify get_pending_balance_to_withdraw](https://github.com/sigp/lighthouse/pull/8302)
- [consensus spec update](https://github.com/ethereum/consensus-specs/pull/4737)
#### Phase 3: Validator Client and PTC
**PR**: [VC block production](https://github.com/sigp/lighthouse/pull/8313)
I added VC support for requesting, signing, and publishing execution payload envelopes in the case of self-building plus the v4 block production endpoints. Both SSZ and JSON paths are implemented, with SSZ preferred for lower decode cost. The Web3Signer gained an envelope signing message so remote signers can participate as well. In summary, when proposer==builder, the VC now publishes a signed CL block and signed envelope in one pass.
**PR**: [Standardized ePBS Beacon Api](https://github.com/ethereum/beacon-APIs/pull/552)
I standardized the ePBS related validator to beacon node communication via beacon api modifications. The spec now supports the v4 `/validator/blocks/{slot}` endpoint, defines clean GET/POST pairs for `ExecutionPayloadBid` and `ExecutionPayloadEnvelope`, plus PTC duties and payload attestation endpoints, while dropping legacy blinded fields that no longer apply. This lets VCs fetch, sign, and publish within a slot. This is one of the PR's I was most satisfied with since it will be helpful to all client teams when building out ePBS.
**PR**: [add payload attestation service](https://github.com/shane-moore/lighthouse/pull/16)
This wires up a VC-side `PayloadAttestationService` that wakes at the 3/4th of slot deadline, does a single `GET /validator/payload_attestation_data/{slot}` to fetch common data, signs for all local PTC members in parallel, then emits a single batch `POST /beacon/pool/payload_attestations`. This turns N+1 calls into 1+1 per slot. The PR adds the HTTP client endpoints and timeouts, plugs the service into `ProductionValidatorClient` alongside, and extends `Web3Signer` so remote signers can handle `PayloadAttestationData`. As to keep memory costs low, the service uses an Inner `Arc` to keep clones cheap and relies on the existing duties map. Most wins are on network roundtrips and concurrency.
Other notable PR's:
- [VC - add ptc duty](https://github.com/sigp/lighthouse/pull/8338)
- [update attestation and sync committee voting and aggregation timing](https://github.com/shane-moore/lighthouse/pull/19)
- [BN PTC Duties API Response](https://github.com/sigp/lighthouse/pull/8415)
#### Phase 4: Networking
**PR**: [add new p2p topics](https://github.com/shane-moore/lighthouse/pull/11)
I added the new ePBS gossipsub topics end-to-end, introducing `execution_payload` (carrying `SignedExecutionPayloadEnvelope`) and `payload_attestation_message` and integrated them into lighthouse’s networking stack up to the beacon node. This includes new topic constants and subscription rules, SSZ decode/encode plus fork-digest checks in `PubsubMessage` so we refuse pre-Gloas digests and map cleanly to `GossipKind::{ExecutionPayload,PayloadAttestationMessage}`. I also touched the `router` and `network_beacon_processor` so these messages reach the beacon processor queue on arrival.
This was a good tour of lighthouse’s `libp2p → lighthouse_network → router → beacon processor` flow.
**PR**: [add ExecutionPayloadEnvelopesByRange RPC](https://github.com/shane-moore/lighthouse/pull/20)
I added two new RPCs for Gloas, `ExecutionPayloadEnvelopesByRangeV1` and `ByRootV1`, adding fork-gated codec paths (SSZ-Snappy), request/response wiring, batched fetching signed envelopes instead of doing a thousand one-offs during backfill sync. The effect is fewer round trips, better compression, and clear hooks for sync orchestration.
Other notable PR's:
- [Gloas modified p2p topics](https://github.com/shane-moore/lighthouse/pull/12)
- [gloas blocks rpc limits resizing](https://github.com/shane-moore/lighthouse/pull/22)
## Path To Glamsterdam 🚀
There is still lots of good work to do to get this project over the finish line. This is great for me since my ambition is to become an ethereum core dev, and I understand that it takes time. Fortunately, I'll be able to keep honing my skills as I build out the following over the coming months:
| Month | Tasks |
|---|---|
| **December** | Wire up BN responses for new/modified Beacon API endpoints; add `payload_attestation_verification.rs` for gossip/RPC; store `SignedExecutionPayloadEnvelope` headers; write envelopes and payload attestations to gossipsub; refactor `block_verification.rs`. <br>- Note that I'll be AFK in Antarctica for the first couple weeks! ☃️
| **January** | Fork choice updates; modify attestation creation to support `data.index` 0 or 1; include PTC attestations in block creation and operation pool. |
| **February** | In-protocol builder path in VC; update Builder API per spec; bid selection between in-protocol and off-protocol bids. |
| **March** | Light client updates; remove header + inclusion proof from `DataColumnSidecar` and adjust processing; burn down TODOs, logging, metrics. |
| **April** | Unit/integration/e2e tests; EF test vectors; devnet dry runs and interop fixes. |
## Cohort Reflections 🪞
I stayed close to the evolving spec, refactoring early when direction changed. Where gaps existed, I turned them into contributions, notably the Beacon API PR that unblocks all clients. In hindsight, with devnet-0 prioritizing self-building, I would have shifted sooner from trusted payments to local building.
Working inside Lighthouse was a highlight, both for adopting its coding patterns and for the distributed systems lessons that came with it. Seeing my PRs upstreamed was energizing, especially knowing they will ship as part of the Glamsterdam upgrade. Along the way, I leveled up on consensus internals and picked up practical async Rust patterns from the codebase. I also stayed active in ePBS breakout rooms and the Ethereum R&D Discord, which helped keep my work aligned with the broader effort.

## Takeaways 🔑
Fight Club has a great line, "You decide your own level of involvement". I thought of this frequently during the EPF because it seemed quite applicable to Ethereum protocol development. Sometimes there's an inflight spec and other times no spec at all. You could look at this as a blocker or an opportunity to get involved. The same thing applies to breakout rooms and Eth R&D Discord. You can watch it passively, or you can build context as to help move discussions along. An example was when we were discussing whether to included trusted payments as part of ePBS in glamsterdam, so I wrote up a [re-cap](https://hackmd.io/@blockshane/BJJL43T2le) of the convo and shared it in the [ePBS discord channel](https://discord.com/channels/595666850260713488/874767108809031740/1423821196302090380) so others could gain context quickly as to make a more informed decision on the next ACDC call.
There are times when you don't know exactly what is the most impactful thing to tackle next. Progress beats perfection. When direction is uncertain, pick a good path, move, and iterate.
If you join the EPF, you can choose your own level of involvement in pushing forward the Ethereum roadmap. If you set your aim high, you'll grow that much more during the cohort. Best of luck to all future EPF-ers!
## Fellowship Thanks 🙏
I am profoundly grateful to Josh and Mario for shepherding us through the cohort. We've been on a journey together since the Ethereum Protocol Study Group back in February and built some fond memories together at EthCC in Cannes. I can't wait to reunite at DevConnect in Buenos Aires, and thanks a million for leading the EPF because you're changing lives!
Massive thank you to the Lighthouse and Sigma Prime crew for the mentorship, patience, and thoughtful reviews throughout the fellowship. Mark, thank you for the long calls and deep dives, your Lighthouse and consensus layer context have been so enlightening and have pushed ePBS to where it is today. Eitan, thanks for jumping onto the ePBS track, I’m excited to see your sync manager changes for payloads via RPC and to keep building together. Sean, I appreciate the nudge at EthCC to take on ePBS in Lighthouse, that conversation was pivotal for me and I’m glad I followed it. I learned a lot, I’m a better developer because of all of your support, and I look forward to contributing more as we head toward Glamsterdam and to collaborating with more Sigma Prime teammates once Fulu is on mainnet.
Finally, thanks to all the core devs participating in the ePBS Eth R&D discord. I've learned so much about the nuances of ePBS from these chats, and I appreciate the responses to any noob questions I've had along the way. I can tell it's been a long journey to get ePBS finally selected as a headliner, and it's happening. Mainnet, here we come! But let's focus on devnet-0 for now :)