Skip to content

Derive default auth data from the builder hostname - #94

Merged
nflaig merged 2 commits into
masterfrom
nflaig/default-auth-data
Sep 18, 2026
Merged

nflaig merged 2 commits into
masterfrom
nflaig/default-auth-data

Conversation

@nflaig

@nflaig nflaig commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Aligns the auth_data default with ethereum/builder-specs#168, the builder's hostname instead of the URL bytes verbatim, and updates the examples accordingly.

@JasonVranek

Copy link
Copy Markdown
Contributor

in apis/builder_config.yaml should switch

- auth_data: "0x68747470733a2f2f6275696c6465722d612e6578616d706c652e636f6d"
to
- auth_data: "0x6275696c6465722d612e6578616d706c652e636f6d"

to show the hex-encoded utf-8 bytes of "builder-a.example.com"

@nflaig
nflaig marked this pull request as ready for review September 17, 2026 08:23
mergify Bot pushed a commit to sigp/lighthouse that referenced this pull request Sep 18, 2026
## Description

Adds the per-validator builder configuration endpoints from [ethereum/keymanager-APIs#88](ethereum/keymanager-APIs#88).

- `GET /eth/v1/validator/{pubkey}/builder_config` returns the configuration in use for the validator.
- `POST /eth/v1/validator/{pubkey}/builder_config` replaces and persists the full configuration on a per-validator basis. It does not merge with the previous entry.
- `DELETE /eth/v1/validator/{pubkey}/builder_config` removes the stored entry, so the validator inherits the global configuration again.

The API stores per-validator entries under `validator_configs` in `builder_definitions.yml`.

Changes made by `POST` and `DELETE` apply to subsequent builder preference publication and Gloas block production without a restart.

When `auth_data` is omitted, global and per-validator builder configurations use the builder URL's lowercase ASCII hostname, following [ethereum/builder-specs#168](ethereum/builder-specs#168), [ethereum/beacon-APIs#642](ethereum/beacon-APIs#642) and [ethereum/keymanager-APIs#94](ethereum/keymanager-APIs#94). URLs with and without a trailing slash produce the same auth data.

Closes #9796
Closes #10076


Co-authored-by: Mark Mackey <mark@sigmaprime.io>
SamAg19 pushed a commit to SamAg19/lighthouse that referenced this pull request Sep 18, 2026
## Description

Adds the per-validator builder configuration endpoints from [ethereum/keymanager-APIs#88](ethereum/keymanager-APIs#88).

- `GET /eth/v1/validator/{pubkey}/builder_config` returns the configuration in use for the validator.
- `POST /eth/v1/validator/{pubkey}/builder_config` replaces and persists the full configuration on a per-validator basis. It does not merge with the previous entry.
- `DELETE /eth/v1/validator/{pubkey}/builder_config` removes the stored entry, so the validator inherits the global configuration again.

The API stores per-validator entries under `validator_configs` in `builder_definitions.yml`.

Changes made by `POST` and `DELETE` apply to subsequent builder preference publication and Gloas block production without a restart.

When `auth_data` is omitted, global and per-validator builder configurations use the builder URL's lowercase ASCII hostname, following [ethereum/builder-specs#168](ethereum/builder-specs#168), [ethereum/beacon-APIs#642](ethereum/beacon-APIs#642) and [ethereum/keymanager-APIs#94](ethereum/keymanager-APIs#94). URLs with and without a trailing slash produce the same auth data.

Closes sigp#9796
Closes sigp#10076


Co-authored-by: Mark Mackey <mark@sigmaprime.io>
@nflaig
nflaig merged commit e5aab3e into master Sep 18, 2026
3 checks passed
@nflaig
nflaig deleted the nflaig/default-auth-data branch September 18, 2026 16:30
frisitano added a commit to eth-act/lighthouse that referenced this pull request Oct 22, 2026
…025 (#54)

* Avoid walking already indexed validators in the monitor (#9995)

## Issue Addressed

The [validator monitor](https://github.com/sigp/lighthouse/blob/115bd16fb565c3df2b169a740fa5754adde00762/beacon_node/beacon_chain/src/validator_monitor.rs#L484-L502) walks the already indexed registry during recent block imports, including with default monitoring and zero monitored validators. Milhouse's `iter().skip(index)` traverses the skipped prefix even when there are no new validators.

## Proposed Changes

Start the iterator at the first unknown index with `iter_from`, keeping absolute validator indices. A regression test covers empty registries, growth, shorter forks, late registration and pending updates for both fixed and progressive lists.

## Additional Info

A local mainnet A/B on 7 Sep 2026 used two Lighthouse followers with separate mock ELs, zero monitored validators and a registry of about 2.36 million validators. Each follower had a 3-CPU quota on the same AMD EPYC-Milan VM. The builds used baseline `1256bd99`, with only this loop changed in the treatment. Each window followed one epoch of settling, with binaries swapped between nodes for the second window.

| Median `import_block` duration | Baseline | Treatment | Matched blocks |
| --- | ---: | ---: | ---: |
| 19:29-19:43 UTC | 199.87 ms | 38.47 ms | 66 |
| 19:53-20:07 UTC, after swap | 191.60 ms | 40.56 ms | 66 |

One first-window trace was unmatched and excluded; baseline logs confirm that block was received and became head. All slow outliers are included.

All 132 matched imports improved. Mean paired import savings were 159.70 ms and 152.68 ms; the monitor child span accounted for 158.24 ms and 150.67 ms respectively.

This measures local import completion with simulated execution validity. The monitor runs under the fork-choice write lock, normally after early attestability. These results do not establish earlier attestations, whole-client CPU savings or the same absolute saving in production.

* Remove buggy parent_root calculations from Gloas block production (#9997)

## Issue Addressed

Gloas block production was calculating the parent_root twice, once before advancing the parent state and once after. The calculation prior to the state advance **would yield the wrong block root** in the case where the parent state was not already advanced. In practice, this didn't occur very often because the state advance timer would make the advanced state available in the state cache.

https://github.com/sigp/lighthouse/blob/1256bd99e1b6c5d1290241849354b7e27dbc0ed9/beacon_node/beacon_chain/src/block_production/gloas.rs#L185-L191

This potentially incorrect block root was used to calculate `should_build_on_full` on the next line:

https://github.com/sigp/lighthouse/blob/1256bd99e1b6c5d1290241849354b7e27dbc0ed9/beacon_node/beacon_chain/src/block_production/gloas.rs#L193-L199

As a result, we would sometimes end up reading `should_build_on_full` for the **grandparent** block rather than the parent. Often, this wouldn't make any difference. The impact of this bug was further mitigated by the fact that we re-calculated the `parent_root` a 2nd time _after_ the state advance:

https://github.com/sigp/lighthouse/blob/1256bd99e1b6c5d1290241849354b7e27dbc0ed9/beacon_node/beacon_chain/src/block_production/gloas.rs#L378-L398

This `parent_root` would always be correct, so the block would always be valid, just possibly building on the wrong empty/full variant.

## Proposed Changes

Calculating the `parent_root` at all is conceptually unnecessary. It was already decided and known when we called `load_state_for_block_production`. This PR threads that value through and removes both re-calculations.

A regression test is added in `gloas_block_production_parent_root_with_unadvanced_state`. I've verified that it fails on unstable and passes with this patch.

## Additional Info

Codex was used, but as you can see from the commit history, there was a lot of tweaking to arrive at the simplest fix.

Some more refactors and cleanups are possible (e.g. removing ReOrgInputs), but I'll do that in a separate PR to avoid muddying this bugfix.

This bug is similar to another one recently found in block prod. I'm going to try to weed them all out:

- https://github.com/sigp/lighthouse/pull/9983

* Ignore exits for all withdrawing validators in Gloas block prod (#9983)

## Issue Addressed

Closes:

- https://github.com/sigp/lighthouse/issues/9981

## Proposed Changes

Filter out exits for validators which _could_ be impacted by any withdrawal triggered in the parent execution payload. See linked issue for details of the bug.

This fix prevents production of invalid blocks (!!) in some scenarios post-Gloas.

## Additional Info

Fix & comments written manually, tests fixed by Codex. Regression test by Codex with manual review.

* Add attestations test with `payload_present` in op pool (#9531)

Add a test in operation pool about the index for the cases with and without payload_present. 

Written with Claude Code and did a self review

Currently blocked waiting for https://github.com/ethereum/consensus-specs/issues/5399 fix
Update: the fix https://github.com/ethereum/consensus-specs/pull/5473 is merged and included in [v1.7.0-alpha.13](https://github.com/ethereum/consensus-specs/releases/tag/v1.7.0-alpha.13), currently pending for Lighthouse update to alpha 13

* Don't return Pending node when fork choice reverts to justified block (#9962)

Closes #9544.

## Description

- `find_head_walk` was filtering virtual EMPTY/FULL children of a PENDING node against `viable_nodes`. Under deep non-finality the justified seed can be non-viable, so those children were dropped and `get_head` returned Pending, which breaks block production.
- Skip the filter for PENDING heads. Viability filtering still applies to real block children from EMPTY/FULL nodes.
- Adds `pending_head_resolves_when_justified_subtree_non_viable` in `gloas_payload.rs`.

* Fix flaky unknown block test (#10001)

## Issue Addressed

Fix flaky CI test:

- https://github.com/sigp/lighthouse/issues/9999

## Proposed Changes

The test was failing due to a race between importing the block on gossip vs importing the block as a result of its `getBlobs` call completing. The `getBlobs` codepath was missing a notification to the reprocess queue, which this PR adds.

There's also a new test added which prevents regression on the `getBlobs` codepath.

## Additional Info

Heavily Codex driven, manually reviewed. Change makes sense to me.

* Gloas spec beta 0 (#10014)

## Issue Addressed

Each commit has a link to the relevant consensus spec PR

* Convert produceBlockV4 to POST and round-trip Eth-Builder-Url (Gloas builder API 4/5) (#9806)

Fourth PR of the Gloas builder API stack (beacon-APIs #630):

- convert `/eth/v4/validator/blocks/{slot}` to POST with an optional
  `BuilderConfig` body (min_bid, builder_boost_factor, direct builders)
- add `POST /eth/v1/validator/builder_preferences` for forwarding signed
  builder preferences
- set `Eth-Builder-Url` on produceBlockV4 responses when a direct-builder bid
  wins, accept it on `POST /eth/v2/beacon/blocks`, and forward the signed
  block to that builder

The validator client still uses the legacy GET methods at this point; it
migrates in the final PR of this stack.

* `engine_getBlobsV4` (#9438)

## Issue Addressed

Implement EIP-8070's engine API calls: `engine_getBlobsV4`.

## Additional Info

Claude was heavily used for prototyping. Gloas support will be added after #9325 is merged.

* Add 0x02 support to validator creation (#9702)

## Issue Addressed

NA

## Proposed Changes

Adds the `--compounding` flag to `lighthouse validator_manager create` to allow creation of validators with 0x02-prefixed withdrawal credentials.

## Additional Info

I had to move some test vectors from Holesky to Hoodi as `ethstaker-deposit-cli` has stopped supporting Holesky.


Co-authored-by: Tan Chee Keong <tanck@sigmaprime.io>

* Standardise FCR metrics (#9987)

## Issue Addressed

Closes #9663

## Proposed Changes

Adopt [beacon-metrics](https://github.com/ethereum/beacon-metrics/blob/master/metrics.md#fast-confirmation) 

| Standard metric | Was | Fires |
|---|---|---|
| `beacon_fast_confirmation_slot` | `beacon_fcr_confirmed_root_slot` | after each FCR run |
| `beacon_fast_confirmation_reorgs_total` | new | head reorgs off a confirmed block |
| `beacon_fast_confirmation_fallbacks_total` | `beacon_fcr_revert_to_finalized_total` | confirmed root reverts to finalized |
| `beacon_fast_confirmation_restarts_total` | `beacon_fcr_restart_from_justified_total` | restart from the observed justified checkpoint |

Added a metric to track head reorgs out of the previous confirmed root:
- `_confirmed_root_reorgs_total`

* Enable FCR test cases (#9964)

## Issue Addressed

Enable remaining FCR test cases

## Proposed Changes

Fix fake_crypto to produce correct states if the input pub keys are valid

## Additional Info

* Cherry Pick Tolerate empty sync aggregates in the basic simulator (#9781)

* Cherry Pick Attempt to fix flaky fallback simulator test (#9908)

* Cherry Pick Update Docker builder to Debian Bookworm (#9984)

* Cherry-pick lint fixes

* broadcast PTC votes early on payload availability (#9434)

- Updated PayloadAttestationService to broadcast PTC votes as soon as the execution payload and data columns are available (via SSE execution_payload_available event).
- Retained the existing deadline as fallback.
- Follows the same early-trigger pattern used by AttestationService.

Addresses #9422 and #9584


Co-authored-by: Eitan Seri- Levi <eserilev@gmail.com>
Co-authored-by: Eitan Seri-Levi <eserilev@ucsc.edu>
Co-authored-by: Tan Chee Keong <tanck@sigmaprime.io>

* Avoid copying unchanged justified balances (#10023)

## Issue Addressed

- `find_head` clones `JustifiedBalances` on every call, even when it hasn't changed.
- During block import, this copy happens while holding the fork choice write lock.

## Proposed Changes

- Use the existing `PartialEq` implementation to only clone `JustifiedBalances` when it changes.

## Additional Info

- An instrumented mainnet run with a mock EL found unchanged balances in **130 of 131 calls**, including all 33 block imports.
- Tested against the parent commit with two mainnet beacon nodes in Kurtosis, using mock ELs and equal CPU and memory limits. Both stayed synced with matching block roots and finalized checkpoints.
- Across **96 block imports per node**, average fork choice time during block import dropped from **31.58 ms to 15.00 ms**, about **53% lower**. This includes `get_head` and the early attester cache update. Both nodes ran on the same dev box.

* Bump `rustls` to fix cargo audit failure (#10052)

## Issue Addressed

Cargo audit failure from `rustls`: https://rustsec.org/advisories/RUSTSEC-2026-0285.html

## Proposed Changes

Bump locked `rustls` version to `0.23.45`

* Revert "Move `SlotAssignment` cache to `CanonicalHead` (#9661)" (#10047)

## Issue Addressed

Per this comment: https://github.com/sigp/lighthouse/pull/9831#discussion_r3974642470, it turns out we don't need the `SlotAssignments` cache for regular fork choice. Attempting to use it in the general case is difficult because we need to deal with the fact that cache may be stale, and that building it while holding a lock could be very slow. This PR reverts dbd2824326af23655a9e50b75046a29739203dac so that the cache is used ONLY for FCR.

## Proposed Changes

Reverted the commit dbd2824326af23655a9e50b75046a29739203dac and resolved conflicts in `beacon_node/beacon_chain/src/canonical_head.rs` mostly relating to the fork choice poisoning (unrelated changes on nearby lines).

## Additional Info

Codex used for conflict res, results checked manually.

* fork choice compliance tests (#9710)

## Issue Addressed

Add consensus spec fork choice compliance tests

Some tweaks were made to get tests passing:
1. For attestation related tests: If attestations are for a future-slot and queued we treat them as import failed.
2. Updated test runners to recompute head before every block import
3. Blocks rejected by block verification instead of fork choice skip fork-choice assertions.
4. We don't re-import block attestations after `on_block`. Lighthouse block import already does this, but via the from-block path, which skips the `validate_target_epoch_against_current_time` validation step
5. We only run complaince tests for fake_crypto + fulu/gloas minimal

* Add base support for Heze block production (#9714)

## Proposed Changes

- Enable Heze block production by constructing `BeaconBlockHeze` in `complete_partial_beacon_block_gloas` (the body is identical to the Gloas beacon block variant)
- This work is a pre-requisite for testing the upcoming FOCIL PRs
- Route Heze block building through the existing Gloas `getPayload` path, because the [engine API spec for Bogota](https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md) defines no new `getPayload` version for this fork. Heze responses are deliberately deserialized into the Gloas containers (see [TODO added](https://github.com/sigp/lighthouse/pull/9714/changes#diff-8b79597c7e1a33167a01f676e50f09dbc40e761dad5b294086ebda40828e1641R1064-R1065)), so the Heze-specific engine types stay unused until the payloads diverge. Likewise, envelope submission still uses the Gloas `newPayloadV5` path
- Add test producing and importing blocks across the Heze boundary epoch

This PR unblocks Heze testing through the beacon chain harness. Previously, with block production stubbed, the harness could not build or advance a Heze chain. This is a blocker for any Heze integration tests, a prerequisite for the upcoming FOCIL PRs' tests.

* Cache slashed justified balances (#9830)

## Issue Addressed

This PR caches slashed justified balances so that we can later use these values in gloas's is_head_weak. Since BalancesCache is not persisted to disk, its superstruct variants and encode/decode implementations are no longer necessary. Instead of storing a vec of balances in the cache, we store JustifiedBalances directly in memory, which also contains the slashed validator balances.

Previously slashed validator balances were zeroed out. Now we add an extra field that keeps track of slashed balances. There is a bit of a gap here that I don't think is worth fixing: A validator slashed between justfied epoch and current epoch balance wont be recorded as a slashed validator in this cache, that is their value will stil be recorded in the normal balances map, but not in the slashed balances map. These values are only recorded at justification. Also valdiators activated after the justified epoch dont show up in this cache as well.

This is a slight deviation from the spec, as the spec reads raw state balances. Its a very minor edge case and I don't think its worth worrying about.

* Add support for engine_forkchoiceUpdatedV5 (#9935)

## Issue Addressed

Part of the FOCIL implementation. This PR adds the `engine_forkchoiceUpdatedV5` ([execution api spec](https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md#engine_forkchoiceupdatedv5)) engine API integration, which enables specifying an inclusion list to the execution client when requesting a payload to be built.

## Proposed Changes

- Add `PayloadAttributesV5`, which extends V4 with `inclusion_list_transactions` ([spec](https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md#payloadattributesv5))
- Add `engine_forkchoiceUpdatedV5` support to the engine API module and expose the capability through `exchange_capabilities`
 - Handle the response's `PayloadStatusV2`, which appends the `inclusionListSatisfied` field (per [spec](https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md#payloadstatusv2))
- Send V5 attributes for Heze slots. Note that `custody_columns` is sent as null, matching [the consensus specs](https://github.com/ethereum/consensus-specs/blob/master/specs/heze/validator.md#L184-L190)
- Add mock execution engine support, rejecting V4 attributes from Heze onwards
- Add Heze boundary tests for preparing a payload, checking if the fcU request that the mock receives is the expected one

## Additional Info

-  `inclusion_list_transactions` is sent empty for now; populating it from the inclusion list store will be added in a follow-up PR

* Fix Gloas nodes failing to resume sync after restart (#9940)

## Description

After a restart, a Lighthouse node can fail to resume syncing on a Gloas network.

This can happen when the node persists a Gloas beacon block before it fully imports the payload envelope. A restart then clears the in-memory pending payload cache. Sync downloads the envelope and custody columns for the block again, but reprocessing the known block does not restore its signed execution payload bid. Lighthouse cannot join the downloaded components, and the lookup can run out of attempts. Dependent blocks then stop progressing.

Custody-column and execution-proof processing now load the bid from the persisted block when the cache is empty. Envelope processing reuses the bid from the block that it already loaded.

* Wire the inclusion list store into BeaconChain (#9960)

## Issue Addressed

Part of FOCIL (EIP-7805). This puts the inclusion list store from #9744 onto `BeaconChain` and adds the read wrappers that will be used by gossip verification, fork-choice enforcement, and block production.

## Proposed Changes

* Add an `inclusion_list_store` field to `BeaconChain`, initialise it from `ChainSpec` in `builder.rs`, and prune it in `per_slot_task`.
* Add `inclusion_list_committee`, which resolves the ordered committee for a slot and returns it together with the dependent root used to key the store.
* Add `get_inclusion_list_transactions`, `get_inclusion_list_bits`, and `is_inclusion_list_bits_inclusive` as wrappers around the store reads.
* Add `BeaconChainError::InclusionListStoreError`.
* Add tests covering each read path, the epoch boundary case, and pruning through `per_slot_task`.

This is independent of #9846, and nothing is calling these wrappers yet. Gossip verification will be the follow-up.

## Additional Info

The wrappers take an anchor block root rather than using the head. `with_committee_cache` only resolves a block's own or next epoch shuffling, while inclusion list reads use `slot - 1`. Once per epoch, that falls into the previous epoch and fails when using the head root. There's a test covering this case.

`with_committee_cache` already gives us the attester shuffling decision root, so we get both the committee and the store key from the same lookup. This is also the root that the producer puts in the message as `dependent_root`.

`get_inclusion_list_transactions` only needs to resolve the shuffling for the dependent root and then discards the committee, since that root comes out of the same lookup.


Co-authored-by: Eitan Seri- Levi <eserilev@gmail.com>

* Add support for engine_newPayloadV6 (#10017)

## Issue Addressed

Part of FOCIL (EIP-7805). This adds `engine_newPayloadV6`, as specified in [Bogota](https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md).

## Proposed Changes

* Add `ENGINE_NEW_PAYLOAD_V6` and the `new_payload_v6` capability.
* Add `inclusion_list_transactions` to `NewPayloadRequestHeze`.
* Add `new_payload_v6_heze` and dispatch Heze requests to it.
* Add `inclusion_list_satisfied` to `PayloadStatusV1` for the new `inclusionListSatisfied` response field.
* Route the method in the mock EL, and fix its Heze fork check, which required V5.
* Add tests for deserialising the response with and without `inclusionListSatisfied`.

Nothing constructs a `NewPayloadRequestHeze` yet, so this doesn't change any behaviour. I also cleaned up the two now stale `TODO(heze)`s.

## Additional Info

I kept `inclusion_list_satisfied` on `PayloadStatusV1` rather than introducing a `PayloadStatusV2`. The new field is `BOOLEAN|null`, and the existing fields already use `Option` for the same reason.

The alternative would be to introduce a versioned superstruct like `PayloadAttributes`, but that would mean making changes outside `execution_layer`. I'm fine with going that way if that's preferred.

The Heze arm in `payload_notifier.rs` will need the transactions from the store. That part follows once #9960 merges.

* Migrate the validator client to the Gloas builder API (Gloas builder API 5/5) (#9807)

Final PR of the Gloas builder API stack:

- sign builder request-auth and preferences (REQUEST_AUTH signing domain,
  web3signer message type)
- add the builder configuration store (`builder_definitions.yml`) and book
  documentation
- add the builder-preferences service and switch block production to
  `POST` produceBlockV4 with a `BuilderConfig` body, threading the
  `Eth-Builder-Url` header through block publication
- remove the now-unused legacy `GET` produceBlockV4 client methods

* Add per-validator builder configuration API (#9864)

## Description

Adds the per-validator builder configuration endpoints from [ethereum/keymanager-APIs#88](https://github.com/ethereum/keymanager-APIs/pull/88).

- `GET /eth/v1/validator/{pubkey}/builder_config` returns the configuration in use for the validator.
- `POST /eth/v1/validator/{pubkey}/builder_config` replaces and persists the full configuration on a per-validator basis. It does not merge with the previous entry.
- `DELETE /eth/v1/validator/{pubkey}/builder_config` removes the stored entry, so the validator inherits the global configuration again.

The API stores per-validator entries under `validator_configs` in `builder_definitions.yml`.

Changes made by `POST` and `DELETE` apply to subsequent builder preference publication and Gloas block production without a restart.

When `auth_data` is omitted, global and per-validator builder configurations use the builder URL's lowercase ASCII hostname, following [ethereum/builder-specs#168](https://github.com/ethereum/builder-specs/pull/168), [ethereum/beacon-APIs#642](https://github.com/ethereum/beacon-APIs/pull/642) and [ethereum/keymanager-APIs#94](https://github.com/ethereum/keymanager-APIs/pull/94). URLs with and without a trailing slash produce the same auth data.

Closes #9796
Closes #10076


Co-authored-by: Mark Mackey <mark@sigmaprime.io>

* Replace EF bootnodes with NodeOps fleet (#10025)

I am on the Ethereum Foundation's Node Infrastructure team (NodeOps). We run the bootnodes
that ship in this repository as the "EF" entries for mainnet, Sepolia and Hoodi. We are
replacing that fleet, and we would like the new records in a release before the Glamsterdam
client releases.

### What changes

- Add five new EF bootnodes per network. Every node serves all three networks.
- Remove the legacy EF entries. The new set is a replacement, not an addition.
- Third-party bootnodes and DNS discovery roots are untouched.

### The new fleet

Five bootnodes, each serving mainnet, Sepolia and Hoodi. They are geographically distributed,
multi-cloud, and dual stack (IPv4 + IPv6). Each node runs discv4 and discv5 on the EL port and
discv5 on the CL port, and the fleet spreads across several discovery implementations so one
implementation bug cannot take the whole set down.

### About the records

- The ENRs are generic bootnode records: no `eth2` field, no `tcp`, sequence number 1. Clients
  dial configured bootnodes regardless of fork, and a static list cannot be re-signed every
  fork, so we do not include fork data on purpose.
- Where an EL list accepts ENRs, we list the ENR. It covers discv4 and discv5 and carries the
  IPv6 endpoint. Enodes appear only where a list needs them, in the discovery-only form
  `enode://<pubkey>@<ip>:0?discport=<port>` (no RLPx listener).
- The records are derived from the node keys and are byte-identical across all the client
  PRs. The canonical copies are the eth-clients PRs (`metadata/bootstrap_nodes.yaml` and
  `metadata/enodes.yaml` for mainnet, sepolia and hoodi).

### Verification

- Each node answers discv4 and discv5 pings from the public internet on the advertised ports.
- The relevant tests in this repository pass with the new lists (details below).
- The legacy nodes stay online until client releases with the new records are widely deployed.
  We will announce the shutdown separately.

### Contact

Chase Wright, EF Node Infrastructure. Discord `MysticRyuujin` (ETH R&D and STEEL servers), or
chase.wright@ethereum.org.

### This repository

**Files:** `common/eth2_network_config/built_in_network_configs/{mainnet,sepolia,hoodi}/bootstrap_nodes.yaml`.

**Semantics:** the EF ENRs (4 / 5 / 6) are replaced by the five new CL ENRs per network.

**Tests:** `cargo test -p eth2_network_config` (decodes every built-in ENR).

* Add license/cla check in mergify (#10049)

If an author does not sign CLA, the PR will be marked as `ready-for-review` (if all other tests passed). Example: https://github.com/sigp/lighthouse/pull/10043

Add a condition to check that if the author has not signed CLA, it is marked as `waiting-on-author`.

* enable mplex by default (#10070)

* engine_forkchoiceUpdatedV4: provide custody columns to the EL (#9547)

## Issue Addressed

[engine_forkchoiceUpdatedV4 supplies the custody columns to the EL so that the EL client can employ a sparse blobpool.](https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#request-4)

## Proposed Changes

Send the current custody columns to the EL on every regular `engine_forkchoiceUpdatedV4`



Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com>

* update: chiado bootnodes enrs (#9923)

## Description

Update `Chiado` bootnode enrs.

* Fix custody backfill N× DataColumnsByRange amplification (#10048)

## Summary
- Fix custody backfill sending the full `DataColumnsByRange` request to every selected peer (N× download amplification).
- Select peers using `request.columns` instead of the wider sampling set.
- Add regression tests for per-peer column subsets and request-vs-sampling peer selection.

Closes #10045

## Test plan
- [x] `FORK_NAME=fulu cargo nextest run -p network custody_backfill`

Co-authored-by: Eitan Seri-Levi <eserilev@ucsc.edu>

* Document won't-fix vote slot reset in V28 to V29 migration (#10108)

## Issue Addressed

shut up LLMs about it

* Fix gossip bid rejection at epoch boundaries and on the head's parent (#10061)

## Issue Addressed

Valid payload bids can be dropped with `EpochOutOfBounds` when the cached head is from the previous epoch. Bids on the head's parent can also be rejected with `InvalidPrevRandao` because validation uses the head's RANDAO mix.

## Proposed Changes

Validate `prev_randao` against the bid's beacon parent using the existing `head_random` and `parent_random` helpers. Add a regression for a head in the previous epoch. Head-parent RANDAO spec coverage is tracked in [consensus-specs#5645](https://github.com/ethereum/consensus-specs/pull/5645).

## Additional Info

The [Gloas gossip rule](https://github.com/ethereum/consensus-specs/blob/a8475719ce77cb269191e327e1f4175c295851ee/specs/gloas/p2p-interface.md#execution_payload_bid) uses the parent post-state's current epoch. Related to #9924.

* Gloas spec v1.7.0-beta.2 (#10114)

## Issue Addressed

Latest spec release and tests.

## Additional Info

Codex used, all code manually reviewed.


Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>
Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com>

* Route fork-choice execution status through fork choice nodes (#10100)

## Issue Addressed

This is a pre-step to add gloas optimistic sync.

Improper execution status reading on gloas nodes, on empty descendants of invalid.

## Proposed changes



Fork-choice execution-status queries took a bare root and answered about the block's `FULL` node. In the two-node (Gloas) model an `EMPTY` head runs an ancestor's payload, so an empty head could report a validity its branch never ran.

- Add `ForkChoiceNode` (opaque root + payload status) and `ExecutionVerdict` (no payload hash).
- Split the query w/ node-aware `get_node_execution_status(node)` vs `get_block_execution_status_assuming_full(root)`.
- Gate attesting/proposing on the actual node (attester/proposer duties, `produce_unaggregated_attestation`, `filter_optimistic_attestation`); assume-`FULL` stays only where there is no node (http_api `execution_optimistic`, sync-committee contributions, `is_healthy`).

V29 (Gloas) execution status isn't tracked yet. Hardcoded `Valid` (`TODO(gloas)`) until the optimistic-payload work adds it.

* Add stateless Gloas block production to the VC (#10036)

## Issue Addressed

#8828 (VC half; the beacon-node half landed in #9568)

## Proposed Changes

Add `--stateless-block-production` (default off). When set, the VC produces Gloas blocks with `include_payload=true`, keeps the returned envelope, blobs and KZG proofs, and after publishing the block signs that envelope and publishes it as `SignedExecutionPayloadEnvelopeContents` through the same node selection as the block. This removes the per-BN envelope cache dependency: the envelope is never fetched, and any beacon node can accept the contents form.

- `ValidatorStore::sign_block` gains `local_payload_root: Option<Hash256>`, the `hash_tree_root` of the locally built payload. Lighthouse's store ignores it.
- With the flag off, behaviour is unchanged.

## Additional Info

`ValidatorStore` is also implemented by DVT clients (e.g. Anchor), where the proposer must commit to its payload root before the block is agreed, which is why the root is passed into `sign_block` rather than derived after publication.

Based on #9807; will retarget `unstable` once it merges.

Verified with `cargo nextest run -p validator_services -p validator_client -p lighthouse_validator_store -p validator_test_rig -p validator_store -p eth2`, `cargo nextest run --release -p lighthouse --test lighthouse_tests validator_client`, `make lint`, and `make cli-local`.

* Add gas limit schedule (#9878)

## Issue Addressed

https://github.com/ethereum/consensus-specs/pull/5533

* Introduce ParentPayloadStatus for the parent payload edge (#10121)

## Issue Addressed

- New enum introduced by https://github.com/sigp/lighthouse/pull/9934 that we can merge ahead of time

Current code silently defaults the gloas fork block parent into either empty or full. This makes the logic of #9934 harder to reason about

## Proposed Changes

Make that edge case explicit and force consumers to make a choice.

* Remove perfidious fn ExecutionStatus::block_hash (#10126)

## Issue Addressed

While implementing Gloas optimistic sync this function `ExecutionStatus::block_hash` has been a cause of confusion and bugs. In unstable a None means either PreMerge of PostGloas, and some code uses it as a fork gate.

## Proposed Changes

- Remove `ExecutionStatus::block_hash` for `Block::block_hash` with a clear return type `PayloadBlockHash`
- Add `PayloadBlockHash` which makes it explicit that the return is only None for PreMerge

* Remove ExecutionStatus predicates with no production callers (#10129)

## Issue Addressed

Make #9934 easier to reason about, no need to map `NotYetRevealed` to anything in prod

## Proposed Changes

Remove ExecutionStatus predicates with no production callers

* Mark the forkchoiceUpdated head hash valid, not the head block (#10131)

Extracted pre-step from https://github.com/sigp/lighthouse/pull/9934

## Issue Addressed

After Gloas validating by block_root is wrong if the head is empty, the EL judges the payload. It's correct and easier to reason about to tell fork-choice which block_hash the EL validated, same as https://github.com/sigp/lighthouse/pull/10130

## Proposed Changes

A VALID forkchoiceUpdated vouches for head_hash, which for a Gloas head on its EMPTY node is an ancestor's payload — so mark the hash valid, not the head block. on_valid_execution_payload(head_hash) resolves every committing node and runs the existing walk; V17 identical, equivocating twins now also promoted, Gloas nodes still a no-op.

* Name invalidated payloads by execution block hash, not by block root (#10130)

Extracted pre-step from #9934

## Issue Addressed

After Gloas invalidating by block_root is wrong if the head is empty, the EL judges the payload. It's correct and easier to reason about to tell fork-choice which block_hash the EL invalidated.

## Proposed Changes

The EL judges payloads, so InvalidationOperation now names them by execution block hash and applies to every block committing to the judged payload (equivocation-safe). The two-step walk becomes find_deepest_node_to_invalidate / find_deepest_executed_node / invalidate_node_and_descendants; V17 behavior is preserved case-for-case, Gloas nodes are swept through unmarked as today (#9934 adds their status). notify_new_payload drops its root param and invalidates from the rejected payload's parent hash, which unifies the block and envelope callers.

* Replay votes through compute_deltas when resetting to optimistic (#10133)

## Issue Addressed

`set_all_blocks_to_optimistic` reimplements sensitive logic from `compute_deltas` and `apply_score_changes`, making #9934 more difficult to implement. 

## Proposed Changes

Just re-use `compute_deltas` and `apply_score_changes` instead of hand-rolling weight propagation.




Co-authored-by: Michael Sproul <michael@sigmaprime.io>

* Apply Beacon API amendments to payload attestation data endpoint (#9959)

## Issue Addressed

Addressed the following 2 checklist items in #9045 
- Amended GET `/eth/v1/validator/payload_attestation_data/{slot}` to use query instead of path
- Amended GET `/eth/v1/validator/payload_attestation_data/{slot}` to return 204 when no block

## Proposed Changes

- Commit https://github.com/sigp/lighthouse/commit/5bb8f47e06e034085cac2996fe67b275c182a071 : The endpoint `validator/payload_attestation_data/` returns 204 incase of an empty block for the slot in accordance to the spec changes mentioned in [beacon-APIs#612](https://github.com/ethereum/beacon-APIs/pull/612). `BlockNotFound` rejection has been removed since it was unused after the change and the `BeaconNodeHttpClient` maps 204 to `None` instead of 404. 
- Commit https://github.com/sigp/lighthouse/commit/9eb3912ef75b843bbfa43d6a791709d221e421c5 : `slot` moves from path to a query parameter for the endpoint in accordance to the spec changes mentioned [beacon-APIs#626](https://github.com/ethereum/beacon-APIs/pull/626)

* Eagerly sign sync committee messages on head events (#9675)

## Proposed Changes

`SyncCommitteeService` currently waits for the sync message due point every slot, even when the head block arrived seconds earlier. Per the [Altair honest validator spec](https://github.com/ethereum/consensus-specs/blob/master/specs/altair/validator.md#prepare-sync-committee-message), a sync committee member should produce the message "either when (a) the validator has received a valid block from the expected block proposer for the current `slot` or (b) `get_sync_message_due_ms()` milliseconds has transpired since the start of the slot", whichever comes first. This applies the head-event trigger that #7892 added for attestations to sync committee messages:

- `sync_committee_service` races a head event against its due timer. Head-triggered slots sign the event's block root (the head monitor only forwards non-optimistic heads) and publish shortly after block arrival instead of at the due point.
- Wires `SYNC_MESSAGE_DUE_BPS_GLOAS = 2500` ([Gloas time parameters](https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#time-parameters)) into `ChainSpec` with a fork-aware `get_sync_message_due_at_slot`, so the timer fallback tightens to 25% of the slot at Gloas.
- Converts head events from mpsc to `tokio::broadcast` with `BeaconNodeFallback::subscribe_to_head_events()` so both services can consume them. The shared `head_event_or_deadline` helper also fixes a busy-spin in the attestation service when the head event channel closes.
- Adds sync committee endpoints to `MockBeaconNode` and service tests on the shared VC harness.

## Additional Info
N/A

## AI Assistance Disclosure

**Tools used**: Claude Code Fable 5

* Notify reprocessing queue after RPC custody column imports (#10128)

## Issue Addressed

An RPC-fetched payload envelope can complete execution verification while remaining pending on custody columns. When RPC custody columns subsequently complete the import, `process_rpc_custody_columns` recomputes the head but does not notify the reprocessing queue.

As a result, attestations waiting for the payload envelope are not released by this import event and can remain queued until the existing fallback runs.

The same RPC completion branch also handles pre-Gloas block imports.

## Proposed Changes

Call the existing `notify_import_after_column` helper after successful import and head recomputation, using the slot and root returned by `AvailabilityProcessingStatus::Imported`.

This matches the existing gossip column completion paths and emits the appropriate event:

- `PayloadEnvelopeImported` for Gloas.
- `BlockImported` for pre-Gloas.

Add regression tests covering deferred envelope import under Gloas and deferred block import under Fulu. The tests check the event type and root, with no additional event in each tested sequence. The Gloas test also checks that no completion event is emitted while the envelope is still awaiting columns.

## Additional Info

Both new regression tests failed before the production change and passed afterward:

```bash
FORK_NAME=gloas cargo test --release -p network \
  --features fork_from_env \
  rpc_columns_notify_after_deferred_envelope_import

FORK_NAME=fulu cargo test --release -p network \
  --features fork_from_env \
  rpc_columns_notify_after_deferred_block_import
```

The existing `release_awaiting_attestations_on_payload_envelope_imported` consumer test also passed. These tests cover the network processor notification and queue consumer separately; they are not a full network E2E test.

AI assistance was used for the regression tests, and this PR description.

* Use block hash in payload invalidation tests (#10137)

## Issue Addressed

Continuation of the ideas in:

- https://github.com/sigp/lighthouse/pull/10130
- https://github.com/sigp/lighthouse/pull/10131

## Proposed Changes

This PR applies block_hash-ification to all payload invalidation tests, preparing for Gloas tests and Gloas optimistic sync.

## Additional Info

Codex wrote the code, I checked it.

* GET `/eth/v1/validator/{pubkey}/graffiti` to return String instead of hex string (#10117)

@yorickdowne reported a bug in Lighthouse KeyManager API where the GET graffiti endpoint is returning a hex string:
https://discord.com/channels/605577013327167508/605577013331361793/1552293386646724652

This PR fixes it to return String. 

Thanks for the report.

Testing result:

Before:
```
{
  "data": {
    "pubkey": "0xab",
    "graffiti": "0x7465000000000000000000000000000000000000000000000000000000000000"
  }
}
```

After:
```
{
  "data": {
    "pubkey": "0xab",
    "graffiti": "te"
  }
}
```

* Enable partial data columns by default on all networks (#9896)

## Proposed Changes

Enable partial data columns by default on all networks.

## Additional Info

Partial data columns have been tested on Hoodi and Sepolia for quite some time now, and have shown to be advantageous. There have been no reports of reduced performance.

* Progressive list type-level limits (#10106)

## Issue Addressed

Trying to solve ProgressiveList DoS vectors.

## Proposed Changes

Go back to type-level limits like before, using:

- https://github.com/sigp/ssz_types/pull/86

## Additional Info

Several imperfections we still need to solve:

- [ ] There's no bound on transactions and we probably need a spec change to safely add one (related: https://github.com/ethereum/consensus-specs/pull/5642#discussion_r4056618820)
- [x] The upstream `ssz_types` initially only enforced the limits in some places. I've now changed it to enforce them everywhere to avoid oversized lists slipping through. For example `ProgressiveVariableList::try_from_iter` didn't enforce the limit but was reachable from indexed attestation JSON decoding (via `quoted_u64_var_list`). See: https://github.com/sigp/ssz_types/pull/86#discussion_r4068836679.


Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com>

* Work around libp2p derive warning on Rust 1.100 (#10153)

Fix CI for new changes in the beta compiler which more aggressively flag unreachable code:

Example CI failure:

- https://github.com/sigp/lighthouse/actions/runs/36512785758/job/109228438187?pr=9934

Upstream libp2p issue:

- https://github.com/libp2p/rust-libp2p/issues/6600

* Re-enable optimistic sync for Gloas payload envelopes (#9934)

## Breaking change

Can we force existing Gloas nodes to resync when updating to this new version? Then we can simplify the migrations as the property `execution_status` has continuity in mainnet from Fulu to Gloas. Otherwise there's an awkward partial disappearance between v29 and v30.

## Issue Addressed

Re-enable optimistic sync for Gloas payload envelopes

## Proposed Changes

Track the status of the payload (FULL) node in ProtoNode::execution_status


Co-authored-by: Michael Sproul <michael@sigmaprime.io>

* Update tests to be spec agnostic (#10051)

## Issue Addressed

Certain tests are written with assumptions about Mainnet preset constants. This works fine when we hard code the spec we use for testing ( such as `let spec = ChainSpec::mainnet()`), but if we decide to switch to compile-time preset selection (by adopting something like #9229), these tests will needlessly fail when run under minimal.

## Proposed Changes

Update offending tests to remove baked-in mainnet constant assumptions, making them spec agnostic.

## Additional Info

This PR will be the first in a series of PRs made in an attempt to shrink the diff size of #9229 to make it more manageable to review.

* Prune finalised payload envelopes (#10015)

## Issue Addressed

Closes #9527.

## Summary

This PR reduces beacon-node storage requirements after Gloas by splitting each
`SignedExecutionPayloadEnvelope` into:

- a persistent `SignedExecutionPayloadEnvelopeSummary`; and
- a prunable `ExecutionPayloadGloas` body.

Once a canonical block is finalized, Lighthouse retains the summary and removes the payload body.
If the complete envelope is requested later, Lighthouse reconstructs it using the retained summary
and the payload body returned by the EL through `engine_getPayloadBodiesByHashV2`.

## Implementation
Slight adjustments were made to `SignedExecutionPayloadEnvelopeSummary` to reduce the calls made to EL, this involved adding the `ExecutionPayloadHeaderGloas` to the summary.

* Keep fork choice finalized checkpoint consistent with split (#10165)

## Issue Addressed

Closes:

- https://github.com/sigp/lighthouse/issues/10142


## Proposed Changes

Simple fix to ensure fork choice's finalized checkpoint remains ahead of the on-disk `split`. We just need to persist fork choice in `after_finalization` prior to running the migration which advances the split.

I think this is a simple and intuitive fix.

## Additional Info

Two further changes which aren't essential but nice to have:

- [x] A new database invariant stating that the persisted fork choice finalized checkpoint remains ahead of the split (f76ebc26689c1b8abe8d07c14201b7aa81abecc2). 
- [x] Try to prevent manual finalization (which breaks the invariant) from causing a startup failure by disabling the endpoint when FCR is enabled. This is imperfect protection because someone could enable FCR after running manual finalization, but it is what it is. Issue here for a more thorough fix: https://github.com/sigp/lighthouse/issues/10166.

* Wire the inclusion_list gossip topic (#9846)

## Issue Addressed

Part of FOCIL (EIP-7805). Adds the `inclusion_list` gossip topic so Heze nodes subscribe to it and route messages into the beacon processor.

## Proposed Changes

- `GossipKind::InclusionList` and the `inclusion_list` topic, subscribed via `core_topics_to_subscribe` when `heze_enabled()`.
- `PubsubMessage::InclusionList` with SSZ encode/decode, plus the gossip cache and whitelist filter entries.
- `Work::GossipInclusionList` and its beacon processor queue, and the router arm that dispatches to it.

## Additional Info

Gossip verification isn't in yet, so for now `process_gossip_inclusion_list` ignores every message and nothing gets forwarded. That lands in a follow-up.

I went with `Box` rather than `Arc` here because the inclusion list store takes ownership of the `SignedInclusionList`.

There's no decode size guard and no configured gossip cache timeout, same as the existing Gloas topics. `MAX_SIGNED_INCLUSION_LIST_SIZE` isn't in the tree yet and it belongs with the req/resp work anyway.

* Restore transitive schema migration code (#10170)

## Proposed Changes

It was impossible to upgrade v29 -> v31 because this code to do migrations one at a time was deleted ( :sweat_smile: by me in https://github.com/sigp/lighthouse/pull/9031).

This PR puts it back. In future if we clean up migrations we should make sure to keep it.

No test changes for now because we just need this ASAP.

* Bump quinn

* Fix FCR restart UX bug (#10160)

## Issue Addressed

When a node restarts it's confirmed root reverts to finalized. This doesn't mean the head will ever re-org out of a confirmed root, but from the POV of the EL it looks like a reversion of FCR.

## Proposed Changes

Impl https://github.com/ethereum/consensus-specs/pull/5673 spec to load the latest confirmed_root from disk. Fixes the issue from above.

Still the confirmed_root can backtrack due to an error or the node losing sync. **just for metrics**, we keep track of the deepest confirmed root and watch head re-orgs from it.

* Release v8.2.3

* Release v8.3.0-rc.0

* Simplify fork boilerplate (#10164)

## Proposed Changes

_Anti-boilerplate LLM goes brrr_

Superstruct has macros for avoiding explicit `match` boilerplate, this PR applies them in a bunch of places where they previously weren't used.

In addition, it uses fork-generic methods (like `SignedBeaconBlock::from_ssz_bytes_by_fork`) in place of fork-specific methods (like `SignedBeaconBlock{ForkName}::from_ssz_bytes`).

The goal is to reduce the amount of line churn when adding a new fork variant, which may also simplify the implementation of some planned code-generator changes.

* Add IL committee duties polling to the VC (#9799)

## Issue Addressed

This PR adds polling of inclusion list committee duties to the VC per [spec](https://github.com/ethereum/consensus-specs/blob/master/specs/heze/validator.md#lookahead). This is part of adding FOCIL support in the Heze fork.

## Proposed Changes

- Update `duties_service` to spawn a dedicated IL duties polling task. This only spawns when the Heze fork is scheduled.
- The task polls IL committee duties for the current and next epoch, each slot, starting with `heze_fork_epoch - 1` epoch.
- Note that this PR uses the same bandwidth optimization as attester/PTC polling: per epoch, it does a small request for one validator, only refetching the full duties list if the `dependent_root` is different than the cached one.
- Duties are cached per epoch, together with the `dependent_root` they were computed against, and they are fully replaced when the dependent root changes.
- The request to the BN is done through `POST validator/duties/inclusion_list/{epoch}`, per [beacon api spec](https://github.com/ethereum/beacon-APIs/pull/490/changes#diff-ceb4d08d1cb30c34a9a58b27ad023c417ed6eb3c35235c5848c67039e5403fc0R1-R71).
- Included metrics: fetch/store/per-epoch timers, IL committee validators count gauge.

## Additional Info

- The BN endpoint support will be added in a follow-up PR, since its addition depends on [PR #9742](https://github.com/sigp/lighthouse/pull/9742) (committee derivation) and [PR #9714](https://github.com/sigp/lighthouse/pull/9714) (Heze block production, needed for the testing setup).

* Keep ENR next_fork_version at a BPO fork (#10161)

## Issue Addressed

The ENR `eth2` field advertises the wrong `next_fork_version` when a BPO fork is scheduled before the next regular fork.

The [Fulu p2p spec](https://github.com/ethereum/consensus-specs/blob/82b6e507dc31f6f7052e79b16ecfe3778c1b38f6/specs/fulu/p2p-interface.md#eth2-field) says that `next_fork_version` "will only change for regular forks, _not BPO forks_". `next_fork_epoch` counts BPO forks. `enr_fork_id` takes `next_fork_epoch` from `next_digest_epoch`, which includes BPO forks. But it takes `next_fork_version` from the next regular fork. Example: the current fork is Fulu, with a BPO fork at epoch A and Gloas at epoch B > A. Lighthouse advertises (Gloas, A). The spec requires (Fulu, A).

Prysm's discv5 dial filter (`compareForkENR`, from OffchainLabs/prysm#15604) rejects a peer when `next_fork_epoch` matches and `next_fork_version` does not. Prysm advertises (Fulu, A). So in this window, Prysm does not dial Lighthouse nodes that it finds through discovery. No public network is in this state today.

Spec issue: ethereum/consensus-specs#5701

## Proposed Changes

- `next_fork_version` is now the fork version in effect at `next_digest_epoch`. If nothing is scheduled, it is the current fork version. A fork epoch at `far_future_epoch` counts as not scheduled.
- Add `enr_fork_id_next_fork_with_bpo`. It checks both fields for these cases: a BPO before, at, and after Gloas, a BPO only, nothing scheduled, a past BPO, pre-Fulu, and Gloas at `u64::MAX`.

## Additional Info

- Before Fulu, `next_digest_epoch` returns the next regular fork's epoch. The result changes only when two regular forks share that epoch, for example Fulu and Gloas at the same epoch. It is then the later fork's version, which is the version in effect at that epoch. Before, it was the earlier fork's version.
- After this change, `ChainSpec::next_fork_epoch` is used only by its own test. I kept it, because it is public API. I can remove it if you prefer.
- Prysm, Lodestar, and Nimbus already use this rule. The same change is open for Teku, Caplin, and Grandine.

* Remove redundant todo (#10195)

## Issue Addressed

N/A

## Proposed Changes

I think we just forgot to remove this conversion. The envelope errors are handled correctly downstream already

* Trust the system CA store for outbound HTTPS (#10110)

## Issue Addressed

#10105

System trust was lost in #4650, restored in #4793 and lost again in #8768 when OpenSSL was removed.

## Proposed Changes

- Enable reqwest's `rustls-tls-native-roots` feature alongside `rustls-tls`, so the shared client loads system roots as well as the bundled webpki roots.
- Update documentation in the Web3Signer guide

> [!NOTE]
No new crate enters the tree: `rustls-native-certs` was already a transitive dependency, so the lockfile only gains two edges.

## Additional Info

One thing that caught us out: if either `SSL_CERT_FILE` or `SSL_CERT_DIR` is set, `rustls-native-certs` loads only from there and skips the platform store. Our base image already sets `SSL_CERT_FILE`, so we had to set `SSL_CERT_DIR` too before the CA we'd mounted into a directory was picked up. `reqwest` adds the bundled webpki roots separately, so public HTTPS keeps working either way.

Whilst a global change, I modified `book/src/advanced_web3signer.md` since thats where `root_certificate_path` is documented. I'm fine with moving it somewhere more general if that's preferred.

### Testing

Tested this against a local rustls server using a private CA. It connects when the CA is in `SSL_CERT_FILE`, connects when it's in `SSL_CERT_DIR`, and still rejects the connection when the CA isn't present. With the feature turned off again it rejects even with `SSL_CERT_FILE` set, which is what we hit on v8.2.2. Public HTTPS kept working throughout.
`cargo check --workspace --all-targets` and `make mdlint` both pass.

* Fix nightly tests failing on phase0 and altair (#10200)

## Issue Addressed

The nightly tests have been failing on phase0 and altair since #10160 (e.g. https://github.com/sigp/lighthouse/actions/runs/36985385774). Six of the new `fast_confirmation_restart` tests fail because FCR never confirms a block ahead of finality, e.g.:

```
FCR should have confirmed a block ahead of the finalized checkpoint
```

Before Bellatrix there's no execution block hash, so `run_fcr` returns `NodeHasNoBlockHash` and the node always falls back to the finalized block:
https://github.com/sigp/lighthouse/blob/d917caf682a24e076f8d4311f435c369043038d7/beacon_node/beacon_chain/src/canonical_head.rs#L1400-L1404

We only run recent forks on PR CI, so this wasn't caught there.

## Proposed Changes

Skip all tests in `fast_confirmation_restart.rs` before Bellatrix, same as we do in `payload_invalidation.rs`. The other tests in the file only pass pre-Bellatrix because both nodes stay at the finalized block, so they don't test anything there.

* Subscribe to next fork topics an epoch early (#10204)

## Description

Subscribes to all topics of the next fork digest one epoch early. Alternative to #9794 where we are just subscribing to `proposer_preferences` 1 epoch early but all other topics are left unchanged.

- Publish proposer preferences on the digest of their proposal epoch.
- Keep the next digest when we unsubscribe from old topics.
- Fix startup and sync catch-up for the next digest.
- Add a test that steps through fork schedules slot by slot.

Closes #9041

* Propagate beacon chain database read errors (#10217)

## Issue Addressed

https://github.com/sigp/lighthouse/issues/10216

## Proposed Changes

Propagate errors from `store_contains_beacon_chain()` during beacon chain
startup instead of treating database read failures as an absent chain. This
preserves the database error for `FromStore` startup rather than selecting the
DepositContract genesis path.

## Additional Info

* Remove deprecated builder `submitBlindedBlock` v1 (#10213)

Deprecated here https://github.com/ethereum/builder-specs/pull/123

* Use payload envelope summary in head snapshot and block production (#10187)

The head snapshot and the proposer re-org path only need fields from the payload envelope summary. Load the summary instead of the full envelope, so these paths no longer read the payload body or fail when it is pruned.

* Add documentation for fast confirmation rule (#10183)

Add some docs to the FCR

* Add compile-time Spec alias and migrate types tests (#10158)

## Issue Addressed

The next step in splitting up #9229, following #10051.

## Proposed Changes

- Add a compile-time `Spec` preset. It is set to `MainnetEthSpec` by default. Other presets are selected with `spec-minimal`/`spec-gnosis`.
- Switch the `types` tests to use `Spec` directly. The rest of the tests will follow in future PRs.
- Add a `spec-minimal` feature to the crates which will need it later.
- Add `make test-spec-minimal` and `make lint-spec-minimal`, along with CI jobs for each.

## Additional Info

Only tests use `Spec`. The binaries still pick their preset when they start, so nothing changes at for production code at runtime, it continues to use EthSpec.

* Revert the execution proof availability gate (#10233)

## Issue Addressed

- Current approach for optional proofs is #10198

## Proposed Changes

- Revert #9808

* Fix Web3Signer Gloas request types for remote-signing-api v1.4.0 (#10207)

## Description

Web3Signer 26.9.0 follows remote-signing-api v1.4.0 for Gloas signing. Lighthouse still sent the old request shapes (`PAYLOAD_ATTESTATION` / `REQUEST_AUTH`, unwrapped payloads), so Web3Signer would reject them.

Rename the message types, wrap Gloas objects as `{version, data}`, and omit `fork_info` for `BUILDER_REQUEST_AUTH`. Add Sepolia Gloas coverage in `web3signer_tests`, pin Web3Signer 26.9.0 from GitHub, and require Java 25 for those tests.

Fixes #9252

## Test plan

- [x] `cargo nextest run -p signing_method --release`
- [x] `cargo nextest run -p web3signer_tests sepolia_gloas_types --release`

* Split up EF tests to run each preset separately (#10237)

## Issue Addressed

Follows on from #10158, another piece of #9229 

## Proposed Changes

Move the EF tests to run separately using compile time preset selection.

* Bump PTC SSE queue (#10239)

## Issue Addressed

Closes:

- https://github.com/sigp/lighthouse/issues/10214

## Proposed Changes

Bump the queue cap to 512 so we can take a whole slot worth of PTC messages in a burst. This does not meaningfully increase memory usage.

* Fix PTC duties at the Gloas fork boundary (#10193)

Closes #9584

At the Gloas fork, the PTC duties endpoint returned 500 `IncorrectStateVariant` while the head state was still Fulu, so the VC missed PTC duties for slot 0 of the fork epoch. The BN now serves fork-epoch duties from the advanced head state at slot 0 and returns 400 before the fork.

https://discordapp.com/channels/595666850260713488/1555299785437610066

* Fix blob ordering when filtering blobs by versioned hash (#9944)

## Issue Addressed

Closes #9941.

## Proposed Changes

- `GET /eth/v1/beacon/blobs/{block_id}` now returns blobs in the order of the KZG commitments in the block, regardless of the order of the requested `versioned_hashes` ([spec](https://github.com/ethereum/beacon-APIs/blob/master/apis/beacon/blobs/blobs.yaml#L8-L9)). This is done by sorting the derived blob indices before fetching the blobs
- Add a test that requests the versioned hashes in reverse order and asserts that the returned blobs follow the commitment order

## Additional Info

- The second point of the issue (response `data` being a list of `{"blob": …}` objects instead of hex strings) does not apply: `BlobWrapper` is `#[serde(transparent)]`, so the `data` field in the response is already a flat array of hex strings. I validated this locally with a throwaway request test against the `http_api` test server (not included in this PR)

* Align the EIP-8025 proof types and gossip rules with the spec (#10236)

## Issue Addressed

This PR represents the final state of EIP-8025 spec https://github.com/ethereum/consensus-specs/pull/5639

## Proposed Changes

Update types, verifier API and gossip rules

* Add inclusion_list_bits to ExecutionPayloadBid (#9729)

This PR is a follow-up on [the base FOCIL types & constants PR](https://github.com/sigp/lighthouse/pull/9708).

## Proposed Changes

- Add `inclusion_list_bits` to the Heze `ExecutionPayloadBid`
- Convert `ExecutionPayloadBid` and `SignedExecutionPayloadBid` to `superstruct(Gloas, Heze)`. Most of the changes in the PR are cascading from this decision, because fields are migrated to getters as part of this change.
- Later edit: post [#9714](https://github.com/sigp/lighthouse/pull/9714) merge, local block production also builds Heze bids

## Additional Info

- `inclusion_list_bits` defaults to empty everywhere, so we're not computing or reading it yet. The main goal is to land this early so that we freeze the Heze bid and state layout before downstream FOCIL work builds on it.
- The Gloas variant is byte-identical to the old struct's version, so the `ssz_static` vectors for the bid and `BeaconState` pass unchanged.
- This PR was tested locally by running the following commands:
  - `make cargo-fmt`
  - `make lint`
  - `make test`
  - `make test-ef`

Co-authored-by: Eitan Seri- Levi <eserilev@gmail.com>

* Add `safe_block_hash` and `finalized_block_hash` to Payload Attributes event (#10244)

## Issue Addressed

Addressed the following item in the checklist #9045 
- Modify `payload_attributes` event to add `safe_block_hash` and `finalized_block_hash`

## Proposed Changes

- Add `safe_block_hash` and `finalized_block_hash` parameters to `SseExtendedPayloadAttributesGeneric`
- If Gloas is not enabled, `safe_block_hash` and `finalized_block_hash` is set to None else they are to `justified_hash` and `finalized_hash` from `forkchoice_update_params` respectively

* update libp2p (#10229)

simplification of https://github.com/sigp/lighthouse/pull/10115/

* Implement optional proofs with fork-choice optimistic (#10198)

## Issue Addressed

Implement the second part of optional proofs. With this code the consumer of proofs is complete assuming recursive proofs. Proofs are produced and seeded into gossip by another client, out of scope of this PR.

- part of https://github.com/sigp/lighthouse/issues/9658

## Proposed Changes

Now that gloas optimistic sync is merged, optional proofs are very easy to support:

- Use existing payload and data availability cache for those two things.
- Once they arrive immediately import with Optimistic status
- Wait for proofs to arrive => once you get enough proofs flip the fork-choice status of that node to VALID

* Make the execution layer optional with proof engine (#10262)

## Issue Addressed

Allow Lighthouse to run without EL if there's a local proof engine and the head is post gloas. We allow nodes to switch between having and not having an EL as long as the latest verified head is Gloas.

- Part of https://github.com/sigp/lighthouse/issues/9658

## Proposed Changes

Stop sending new payload, and FCUs. Do no attempt local block production. Automatically disable get_blobs optimization.

## Additional Info

Please provide any additional information. For example, future considerations
or information useful for reviewers.

* Add POST /eth/v1/beacon/execution_proofs (#10210)

## Description

Adds `POST /eth/v1/beacon/execution_proofs`: an SSZ list of `SignedExecutionProofEnvelope`, each gossip-verified, published and cached. EIP-8025 proofs enter the network by submission. A prover signs and submits on its own schedule, since a block's first proof lands minutes after the block.

Spec: ethereum/beacon-APIs#660.

- part of https://github.com/sigp/lighthouse/issues/9658

## Notes

Flagged as work in progress until spec is merged ethereum/beacon-APIs#660

* Split `beacon_chain` tests by preset (#10246)

## Issue Addressed

The next step in splitting up #9229, following #10237.

## Proposed Changes

- Switch the `beacon_chain` tests to `Spec`.
- Split the `beacon_chain` tests between those run in the `minimal` preset and those run under the `mainnet` preset.

* Fix empty BlocksByRoot responses during Gloas column publish (#9976)

## Description

- Gloas skipped DA pre-import cache insert, so columns could publish while `BeaconBlocksByRoot` returned empty and peers downscored/disconnected
- Cache the Gloas block in `pending_payload_cache` (not the DA checker) and serve it via `get_block_process_status`
- On gossip, insert before spawning getBlobs/column publish; in `process_block`, insert after consensus verification
- Gloas import still does not wait on custody columns

Closes #9975

## Test plan

- [x] `cargo nextest run -p beacon_chain --release --test beacon_chain_tests gloas_pre_import_rpc_cache`

* Gloas progressive merkle proofs (#9790)

## Issue Addressed

Implement EIP-7688 progressive container Merkle proofs

AI disclosure: Written by claude fable 5


Co-authored-by: dapplion <guillem.lion.benosa@gmail.com>

* Split `http_api` tests by preset (#10284)

## Issue Addressed

The next step in splitting up #9229, following #10246.

## Proposed Changes

- Switch the `http_api` tests to `Spec`, so they run with their compile-time preset.
- Split the `http_api` integration tests into two, one for each preset.

* Fix cross builds after cc bump (#10278)

## Issue Addressed

#10229 set `CXX = "clang++"` in `.cargo/config.toml` to work around LevelDB's `-Wthread-safety` probe. The `cross` images only ship GCC, so all cross builds (docker, release, binary-builder) on `unstable` now fail in `leveldb-sys`:

- x86_64: `failed to find tool "clang++"`
- aarch64: the image's `CXX_aarch64_unknown_linux_gnu` takes precedence, so GCC is used and hits the original `-Wthread-safety` error

## Proposed Changes

- In `Cross.toml`, use `g++` inside cross containers.
- Apply the LevelDB workaround through a small CMake wrapper (`scripts/cross/cmake-wrapper.sh`) that pre-seeds `HAVE_CLANG_THREAD_SAFETY=OFF`.

## Additional Info

Tested on a fork: `lighthouse` and `lcli` `maxperf` builds pass for x86_64 and aarch64.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* Unify `BlobInfo` and `DataColumnInfo` into `DataInfo` (#9957)

## Issue Addressed

Pruning can behave weirdly on freshly synced nodes, as the `oldest_blob_slot` is not correctly updated.

## Proposed Changes

To avoid this and possibly other related issues, we unify the two separate markers into one: `DataInfo`.

Details:

- `DataInfo` is now initialized in a way similar to the split: `open` does not set any presumptive value if `DataInfo` is not set, but relies on the initialisation method (migration, checkpoint sync, genesis sync) to set an appropriate value
- `oldest_data_slot` is not an `Option` - nowadays nodes only operate with Fulu in the past (we explicitly dropped support for running during earlier forks when we ripped out Blob gossiping etc). So this always has a sensible value, except during initialization - similar to the split.

## Additional Info

K-46

* Resolve the fork from the block slot when loading the http-sync cache (#10276)

## Issue Addressed

Closes https://github.com/sigp/lighthouse/issues/10275…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants