# cl-misc merge 01: Lighthouse, Prysm and Teku vs the Gloas spec (EIP-8282, EIP-7688, EIP-8045, EIP-8061)

This merges three item reviews: `item-lighthouse`, `item-prysm` and `item-teku`, all dated 2026-10-27. All three were present and non-empty. Each was a public-page, read-only review: no item ran anything against a network or a node. This merge adds no findings of its own and drops no candidate. Candidate text is copied from the items. The only edits are that reference-style links in the Teku item are written out inline, and URL ledgers are dropped, except for the pins.

**Citation caveat (Lighthouse item).** The Lighthouse worker had no shell and read files through a fetch tool that could not count lines. Its citations are `path@commit` + function name, with **no line ranges**. Its quoted lines are exact strings from that tool, not checked byte for byte against a checkout. The Prysm and Teku items do give line numbers for the Lighthouse files they cross-read, at `stable` `a376defa…`.

## 1. Pins

| Client | Branch | Commit | Spec commit (ethereum/consensus-specs `master`) | Pinned by item |
|---|---|---|---|---|
| sigp/lighthouse | `unstable` (reviewed) | `301f3bf2323cd2b3f946ceed026be8075d24e1c3` (2026-10-27) | `8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2` | lighthouse |
| sigp/lighthouse | `stable` | `a376defa30470923556d91bdeb15a802e297f31f` (Release v8.3.2 #10433; 2026-10-22 per lighthouse/teku items, 23:27:50Z per teku; listed as 2026-10-23 by prysm item) | same | lighthouse, prysm, teku (cross-reads) |
| OffchainLabs/prysm | `develop` | `eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a` (2026-10-27 15:19 UTC) | same | prysm, lighthouse (cross-reads) |
| Consensys-Incorporated/teku | `master` | `2c35574b3373cbe8ad121a768d98f066aec05b73` (2026-10-27 16:51:17Z) | same | teku |

Other pins, from the teku item:
- consensus-specs release `v1.7.0-beta.4` = `aa16bb4c156184e9548a997d53efaf2a228a6304`.
- ethereum/EIPs `master` = `d4c575521d99ec881a444ab3af604e684b5f0743`.
- ethereum/execution-specs `forks/bogota` = `7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b`.

The spec master pin `8a80ffa8…` is dated 2026-10-27 16:28:12Z, with head message "Fix block access list hash in EL block hash test helper (#5773)". All three items used the same spec pin. Pins came from `api.github.com/repos/<repo>/commits/<branch>`.

**Which spec release each client targets.** All three clients target **v1.7.0-beta.4**, not master:
- Lighthouse: PR #10307 "Gloas spec v1.7.0-beta.4", merged 2026-10-12. Also `testing/ef_tests/Makefile#L3` at stable reads `CONSENSUS_SPECS_TEST_VERSION ?= v1.7.0-beta.4` (prysm item).
- Prysm: `WORKSPACE#L285` reads `consensus_spec_version = "v1.7.0-beta.4"`, and `.ethspecify.yml#L1` reads `version: v1.7.0-beta.4`.
- Teku: [build.gradle#L528-L534](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/build.gradle#L528-L534), with a separate nightly option.

Spec PR ethereum/consensus-specs#5736, "Separate builder and validator deposits at the Gloas fork" (fixes #5735), was merged 2026-10-26T19:16:28Z as merge commit `8b374fa8…`. It is on master but not in beta.4.

The items agree that the only *behavioral* Gloas change between beta.4 and master is the PR 5736 onboarding change. The teku item adds two details: there is also a nonbehavioral EIP-7521 → EIP-7251 docstring fix in beacon-chain.md, and validator.md, p2p-interface.md and weak-subjectivity.md are byte-identical to beta.4.

**Lighthouse stable vs unstable** (lighthouse item). At stable, two things were re-read and found the same as unstable:
- `upgrade/gloas.rs`: same onboarding order and the same `proposer_lookahead` move.
- `execution_requests.rs`: same fields, limits, `progressive_container` / `active_fields(1,1,1,1,1)` and `RequestType` 0..4 mapping.

For every other function, only the unstable result is established.

**Scope inventory** (teku item, from [EIP-7773](https://github.com/ethereum/EIPs/blob/d4c575521d99ec881a444ab3af604e684b5f0743/EIPS/eip-7773.md#L21-L55)):
- Scheduled core EIPs: 2780, 7688, 7708, 7732, 7778, 7843, 7928, 7954, 7976, 7981, 7997, 8024, 8037, 8038, 8045, 8061, 8246, 8282.
- Status of the four EIPs in scope: 7688 is in Review; 8282, 8045 and 8061 are in Last Call. The lighthouse item adds that the EIP-8282 Last Call deadline is 2026-11-01.

## 2. Candidates

Each candidate has the six parts from its item, then its known-check. Candidate prefixes: `lighthouse-n` = item-lighthouse Cn, `prysm-n` = item-prysm Cn, `teku-n` = item-teku Cn. `teku-4` is the Teku item's unnumbered "Restricted entry".

### lighthouse-1. Spec-version lag: fork-time builder onboarding follows v1.7.0-beta.4, not master (EIP-8282)

**(1) Spec (master), `specs/gloas/fork.md` `onboard_builders_from_pending_deposits`.**
Source: https://raw.githubusercontent.com/ethereum/consensus-specs/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/fork.md

```python
    for deposit in state.pending_deposits:
        # Only builder-credential deposits can fund builders at the fork.
        if not is_builder_withdrawal_credential(deposit.withdrawal_credentials):
            pending_deposits.append(deposit)
            continue
        builder_pubkeys = [builder.pubkey for builder in state.builders]
        if deposit.pubkey not in builder_pubkeys:
            if not is_valid_deposit_signature(
                deposit.pubkey, deposit.withdrawal_credentials, deposit.amount, deposit.signature,
            ):
                # Preserve validator top-ups when no builder can receive them.
                if deposit.pubkey in validator_pubkeys or is_pending_validator(
                    pending_deposits, deposit.pubkey
                ):
                    pending_deposits.append(deposit)
                continue
            add_builder_to_registry(...)
        else:
            builder_index = BuilderIndex(builder_pubkeys.index(deposit.pubkey))
            state.builders[builder_index].balance += deposit.amount
```

The same function at v1.7.0-beta.4 (https://raw.githubusercontent.com/ethereum/consensus-specs/v1.7.0-beta.4/specs/gloas/fork.md) checks things in a different order:
1. `if deposit.pubkey in validator_pubkeys:` keeps the deposit and continues.
2. `if deposit.pubkey not in builder_pubkeys:` then:
   - a non-builder credential keeps the deposit;
   - `if is_pending_validator(pending_deposits, deposit.pubkey):` keeps the deposit;
   - an invalid signature makes it `continue` (the deposit is dropped);
   - otherwise `add_builder_to_registry`.
3. `else:` credits the existing builder, whatever the credential.

**(2) Lighthouse.** `consensus/state_processing/src/upgrade/gloas.rs`, fn `onboard_builders_from_pending_deposits`, at unstable `301f3bf2…` (identical order at stable `a376defa…`).
Source: https://raw.githubusercontent.com/sigp/lighthouse/301f3bf2323cd2b3f946ceed026be8075d24e1c3/consensus/state_processing/src/upgrade/gloas.rs

```rust
for deposit in &current_pending_deposits {
    if state.get_validator_index(&deposit.pubkey)?.is_some() {
        pending_deposits.push(deposit.clone());
        continue;
    match builder_pubkey_to_index.get(&deposit.pubkey).copied() {
        None => {
            if !is_builder_withdrawal_credential(deposit.withdrawal_credentials, spec) {
                pending_deposits.push(deposit.clone());
                if is_valid_deposit_signature_cached(builder_onboarding_cache, deposit, spec) {
                    valid_pending_validator_pubkeys.insert(deposit.pubkey);
                continue;
            if valid_pending_validator_pubkeys.contains(&deposit.pubkey) {
                pending_deposits.push(deposit.clone());
                continue;
            if !is_valid_deposit_signature_cached(builder_onboarding_cache, deposit, spec) {
                continue;
            builder_pubkey_to_index.insert(deposit.pubkey, builders.len() as u64);
            builders.push(Builder::from_deposit(
        Some(builder_index) => {
            builder.balance.safe_add_assign(deposit.amount)?;
```

The fetch tool returned these as separate lines, with closing braces left out. This is the beta.4 order. (Line anchors at stable from the other items: `consensus/state_processing/src/upgrade/gloas.rs#L258-L304` (prysm item) / `#L258-L303` (teku item).)

**(3) Conditions where the post-fork state differs from master** (all in the upgrade at the Gloas fork epoch). The state at the fork has `pending_deposits` containing any one of the following:
- **(a)** A deposit with 0xB0 credentials and a valid signature whose pubkey already belongs to a validator.
  - Master: registers or tops up a builder and removes the deposit from the validator queue.
  - Lighthouse: leaves the deposit in the validator queue, where it later tops up the validator.
- **(b)** A deposit with 0xB0 credentials and a valid signature whose pubkey has an earlier valid non-builder deposit in the queue.
  - Master: creates the builder.
  - Lighthouse: keeps it queued for the validator.
- **(c)** A deposit with non-0xB0 credentials whose pubkey was registered as a builder by an earlier deposit in the same queue walk.
  - Master: keeps it in the validator queue.
  - Lighthouse: credits it to the builder's balance.

In each case `state.builders`, the builder balances and `state.pending_deposits` differ, so the first Gloas state root differs. Every one of these needs a deliberately crafted deposit pair before the fork. Ordinary deposits do not trigger them.

**(4) Prysm agrees with Lighthouse (beta.4), not with master.**
`beacon-chain/state/state-native/setters_gloas.go`, fn `OnboardBuildersFromPendingDeposits`, at `eb681c1d…`.
Source: https://raw.githubusercontent.com/OffchainLabs/prysm/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/state/state-native/setters_gloas.go

The order is:
1. `if _, ok := b.validatorIndexByPubkey(pubkey); ok {` keeps the deposit pending.
2. `builderIdx, isExistingBuilder := b.builderIndexByPubkey(pubkey)` tops up an existing builder.
3. `if !helpers.IsBuilderWithdrawalCredential(deposit.WithdrawalCredentials) {` keeps it pending.
4. `isPending, err := pendingIdx.HasValid(deposit.PublicKey)` keeps it pending.
5. The signature is checked in `applyDepositForNewBuilder`.

**(5) Confidence: high** that the code differs from master; the order is quoted from both. I tried to disprove it in four ways:
- I looked for a newer Lighthouse onboarding change. Search for PRs created after 2026-10-20 mentioning gloas found 8, none touching onboarding.
- I checked whether master merely reorders code with the same outcome. It does not: PR #5736's own description lists exactly cases (a)/(b) as behaviour changes ("previous routing wrongly blocked valid builder registrations when a validator already used the key, or when an earlier valid validator deposit was queued").
- I checked whether it is reachable at all. Yes, through any 0xB0 deposit made before the fork.
- I checked what release Lighthouse targets: v1.7.0-beta.4, not master, by its own PR #10307.

This is a **spec-version lag**, not a bug against the release Lighthouse targets.

**(6) Severity: consensus split** at the fork-transition state, if any client ships master semantics while Lighthouse ships beta.4. Today Prysm matches Lighthouse.

**Known-check:** **not found** in Lighthouse. Queries:
- `repo:sigp/lighthouse onboard builders`: 5 hits (#9943 open issue, "deposit cache seeds the whole pending deposit queue", performance; #9954 merged; #9311 closed; #9511 merged; #3725).
- `repo:sigp/lighthouse 5736`: only unrelated issue #5736 from 2024.
- `repo:sigp/lighthouse builder deposit`: 64 hits, first 16 read (#9817 merged 2026-08-30, the onboarding cache, already indexed; #9946 closed; #10162 merged).
- `repo:sigp/lighthouse is:pr created:>2026-10-20 gloas`: 8 hits, none on onboarding.

Spec side: consensus-specs#5736 merged 2026-10-26, fixing #5735. Lighthouse's target is #10307 (merged 2026-10-12, "Gloas spec v1.7.0-beta.4").

### lighthouse-2. EIP-8061 `compute_min_epochs_for_block_requests` not implemented (no Gloas formula for the envelope serving window)

**(1) Spec**, `specs/gloas/p2p-interface.md`
(https://raw.githubusercontent.com/ethereum/consensus-specs/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/p2p-interface.md):

```python
def compute_min_epochs_for_block_requests() -> Epoch:
    numerator = 3 * CHURN_LIMIT_QUOTIENT_GLOAS * CONSOLIDATION_CHURN_LIMIT_QUOTIENT
    denominator = 2 * CONSOLIDATION_CHURN_LIMIT_QUOTIENT + 3 * CHURN_LIMIT_QUOTIENT_GLOAS
    return MIN_VALIDATOR_WITHDRAWABILITY_DELAY + (numerator // denominator) // 2
```

Under `ExecutionPayloadEnvelopesByRoot v1`, the spec adds: clients "MUST support requesting payload envelopes on the epoch range `[max(GLOAS_FORK_EPOCH, current_epoch - compute_min_epochs_for_block_requests()), current_epoch]`".

With mainnet values: 3·32768·65536 = 6,442,450,944 and 2·65536 + 3·32768 = 229,376. The quotient is 28,086, halved to 14,043, plus 256 gives **14,299 epochs**. My own arithmetic.

**(2) Lighthouse.**
- `consensus/types/src/core/chain_spec.rs` (unstable pin): the only definition is the static config value, `const fn default_min_epochs_for_block_requests() -> u64 {` … `33024`. There is no `compute_min_epochs_for_block_requests` and no fork-dependent variant.
- `beacon_node/network/src/network_beacon_processor/rpc_methods.rs`: the envelope handlers `handle_payload_envelopes_by_range_request_inner` and `handle_payload_envelopes_by_root_request_inner` have no oldest-epoch bound. The only checks are `if !fork_name.gloas_enabled() {` ("Requested envelopes for pre-gloas slots") and the finalized/backfill `ResourceUnavailable` path in `get_block_roots_for_slot_range`.

**(3) When results differ.** Lighthouse never applies the Gloas window of about 14,299 epochs.
- It serves envelopes from as far back as it holds, which is a superset of the MUST range and still compliant.
- If Lighthouse uses 33,024 to decide what to request, or how to judge peers that prune at the shorter Gloas window, sync or peer scoring would differ. That could happen past about 14,299 epochs after the fork. I did not confirm this.
- No state root or block validity is affected.

**(4) Prysm:** not checked (time). (The prysm item, prysm-2, independently finds Prysm also uses a fixed 33024.)

**(5) Confidence: medium** that the formula is missing; I searched `chain_spec.rs` and the RPC handler file. **Low** that any visible behaviour follows. As a disproof I looked for an envelope-specific bound in the handlers and found none. Lighthouse issue #4553 "Implement min_epochs_for_block_requests" is still open, which suggests Lighthouse does not prune by this window at all.

**(6) Severity: cosmetic** (p2p serving window or peer scoring only).

**Known-check:** **related known**: #4553 "Implement min_epochs_for_block_requests" (open issue, 2023, not Gloas-specific). Queries:
- `repo:sigp/lighthouse min_epochs_for_block_requests`: 9 hits.
- `repo:sigp/lighthouse 8061`: 6 hits (#9218 open "Clean up churn limit code", #9211 closed, #9190 merged; none about the envelope window).

### lighthouse-3. EIP-7688 limits enforced at decode instead of the state transition (spec-allowed; a narrow difference remains for envelopes)

**(1) Spec.**
- Types: `class BuilderDepositRequests(ProgressiveList[BuilderDepositRequest]):` (no type-level limit).
- `apply_parent_execution_payload`: `assert len(requests.withdrawals) <= MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD`, `… builder_deposits) <= MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD`, `… builder_exits) <= MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD` (beacon-chain.md).
- p2p-interface.md, `verify_execution_requests_limits`: "*Note*: These checks MAY be performed when deserializing `ExecutionRequests`."

**(2) Lighthouse.** `consensus/types/src/execution/execution_requests.rs` (both pins):

```rust
pub deposits: ProgressiveVariableList<DepositRequest>,
pub withdrawals: ProgressiveVariableList<WithdrawalRequest, E::MaxWithdrawalRequestsPerPayload>,
ProgressiveVariableList<BuilderDepositRequest, E::MaxBuilderDepositRequestsPerPayload>,
ProgressiveVariableList<BuilderExitRequest, E::MaxBuilderExitRequestsPerPayload>,
```

The doc comment reads: "uses progressive lists (EIP-7688) with decoder limits, except for deposit requests which are unbounded".

**(3) When results differ.** Only one case: an `ExecutionPayloadEnvelope` that arrives over **req/resp rather than gossip** and carries more requests than the limits.
- The spec has an envelope-level count check only for gossip, `[REJECT] The execution request counts are within their limits`. The length assert runs later, when the child applies the parent payload.
- Lighthouse fails to decode such an envelope, so it never records the payload as present. That could make payload-presence views in fork choice differ.
- For the block body and for gossip, the verdicts agree: the spec explicitly allows decode-time checks, and the state transition asserts the same bounds.
- It is practically unreachable: the EL would have to emit more than 16 or 64 requests, and the bid's `execution_requests_root` would have to commit to them.

**(4) Prysm:** not checked.

**(5) Confidence: low.** As a disproof I found the spec Notes that allow decode-time checks, which rule out the block and gossip paths. The preset limits are equal: Lighthouse `presets/mainnet/gloas.yaml` and spec `presets/mainnet/gloas.yaml` both give 64 builder deposits and 16 builder exits per payload. Lighthouse PR #10106 restored type-level limits on purpose.

**(6) Severity: cosmetic** (fork-choice divergence only in a contrived, likely unreachable case).

**Known-check:** **known / intentional**: #10106 "Progressive list type-level limits", merged 2026-09-29. It notes that Transactions remain unbounded. Already indexed.

**Lighthouse live-code note** (from the item). The churn, proposer-selection and execution-request decoding functions have pre-Gloas branches shared with the live Fulu fork:
- `compute_proposer_index`;
- `get_activation_exit_churn_limit` for Electra/Fulu;
- request types 0x00–0x02.

No candidate was found in those branches, so nothing is withheld for Lighthouse.

### prysm-1. Spec-version lag: builder onboarding at the Gloas fork follows beta.4, not master (EIP-8282 / EIP-7732 fork logic)

Short forms: P = `https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/`, S = `https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/`, L = `https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/`.

**(1) Spec.** S`specs/gloas/fork.md`, function `onboard_builders_from_pending_deposits`. Master reads:
```
        # Only builder-credential deposits can fund builders at the fork.
        if not is_builder_withdrawal_credential(deposit.withdrawal_credentials):
            pending_deposits.append(deposit)
            continue
        ...
        if deposit.pubkey not in builder_pubkeys:
            if not is_valid_deposit_signature(...):
                # Preserve validator top-ups when no builder can receive them.
                if deposit.pubkey in validator_pubkeys or is_pending_validator(
                    pending_deposits, deposit.pubkey
                ):
                    pending_deposits.append(deposit)
                continue
            add_builder_to_registry(...)
        else:
            ...
            state.builders[builder_index].balance += deposit.amount
```
Master's prose adds: "Other deposits stay queued, even if a builder uses the same public key." and "A valid builder registration can use a validator's public key."

Beta.4 (https://raw.githubusercontent.com/ethereum/consensus-specs/v1.7.0-beta.4/specs/gloas/fork.md) has these rules instead:
- It first runs "Deposits for existing validators stay in the pending queue / if deposit.pubkey in validator_pubkeys: … continue".
- It checks the 0xB0 prefix and `is_pending_validator` only for pubkeys that are not yet builders.

**(2) Prysm.** P`beacon-chain/state/state-native/setters_gloas.go#L795-L842`. The annotation at L743 is `<spec fn="onboard_builders_from_pending_deposits" fork="gloas" hash="e09a82dc">`.
```
		if _, ok := b.validatorIndexByPubkey(pubkey); ok {
			newPendingDeposits = append(newPendingDeposits, deposit)
			pendingIdx.Add(deposit)
			continue
		}
		builderIdx, isExistingBuilder := b.builderIndexByPubkey(pubkey)
		if isExistingBuilder {
			if err := b.increaseBuilderBalance(builderIdx, deposit.Amount); err != nil {
		...
		if !helpers.IsBuilderWithdrawalCredential(deposit.WithdrawalCredentials) {
		...
		isPending, err := pendingIdx.HasValid(deposit.PublicKey)
		...
		if isPending {
			newPendingDeposits = append(newPendingDeposits, deposit)
```
Prysm calls this from the upgrade at P`beacon-chain/core/gloas/upgrade.go#L132`.

**(3) When the results differ.** Prysm and master give different post-fork states (`builders`, `pending_deposits`, and so the state root) in three cases. Each needs a deposit still pending at the last Fulu epoch boundary:
- **(a) Builder deposit with an existing validator's pubkey.** The deposit has 0xB0 credentials and a valid signature, and its pubkey belongs to an existing validator. Master registers a new builder with that pubkey. Prysm leaves the deposit queued for the validator.
- **(b) Non-0xB0 deposit for a pubkey that just became a builder.** The deposit has non-0xB0 credentials (e.g. 0x01/0x02), and its pubkey was registered as a builder earlier in the same onboarding pass. Master keeps the deposit queued. Prysm credits it to the builder's balance and removes it from the queue.
- **(c) Valid builder deposit after a pending validator deposit.** The deposit has 0xB0 credentials and a valid signature, and an earlier pending deposit with a valid signature exists for the same new (not-yet-validator) pubkey with validator credentials. Master registers the builder, because only an *invalid* builder signature falls back to the pending-validator check. Prysm keeps the deposit queued as a validator top-up.

**(4) Lighthouse.** L`consensus/state_processing/src/upgrade/gloas.rs#L258-L304` gives the same results as Prysm. It follows beta.4:
- `// Deposits for existing validators stay in the pending queue.` then `if state.get_validator_index(&deposit.pubkey)?.is_some() { pending_deposits.push(...); continue; }`
- `Some(builder_index) => { ... builder.balance.safe_add_assign(deposit.amount)?; }`, which credits the builder whatever the deposit's credential prefix.

**(5) Confidence: high**, that this is a deviation from master.
- I tried to disprove it by diffing beta.4 against master. The diff touches exactly these lines, and Prysm's control flow matches beta.4 line for line.
- It is a lag behind master, not a bug against the release Prysm pins.
- It matters only if master's rule is what ships at the fork.

**(6) Severity:** consensus split at the Fulu→Gloas transition between master-conformant clients and beta.4 clients. It applies only when one of the deposit patterns above is pending at the fork epoch. Today Prysm and Lighthouse agree with each other.

**Known-check:** **not found.** Queries (repo:OffchainLabs/prysm, issues and PRs, open and closed): "onboard builders", "builder deposit", "v1.7.0-beta.5", "5736", "8282".

Related hits, not the same issue:
- #16796 "Defer signature validation when onboarding builders at the Gloas fork" (closed PR);
- #17440 "Pre-warm pending deposit signatures before the Gloas fork" (closed PR);
- #17129 "Restrict builder deposits to payload builders" (closed PR);
- #17147, a cross-fork state-diff pending-deposits bug (closed issue).

Three queries hit the unauthenticated rate limit and returned no results: "beta.4 fork", "builder onboarding validator pubkey" and "weak subjectivity".

### prysm-2. Gloas change to `compute_min_epochs_for_block_requests` not implemented (EIP-8061)

**(1) Spec.** S`specs/gloas/p2p-interface.md`, function `compute_min_epochs_for_block_requests`:
```
    # [Modified in Gloas:EIP8061]
    numerator = 3 * CHURN_LIMIT_QUOTIENT_GLOAS * CONSOLIDATION_CHURN_LIMIT_QUOTIENT
    denominator = 2 * CONSOLIDATION_CHURN_LIMIT_QUOTIENT + 3 * CHURN_LIMIT_QUOTIENT_GLOAS
    return MIN_VALIDATOR_WITHDRAWABILITY_DELAY + (numerator // denominator) // 2
```
With the mainnet values (32768, 65536, 256), this is 256 + (6442450944 // 229376) // 2 = 256 + 14043 = **14299** epochs. I did this arithmetic myself.

**(2) Prysm.** P`config/params/mainnet_config.go#L403` reads `MinEpochsForBlockRequests: 33024, // MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT / 2`. This is a fixed value that does not change by fork. It is used for block retention and serving at:
- P`beacon-chain/db/pruner/pruner.go#L41`
- P`beacon-chain/p2p/custody.go#L142`
- P`beacon-chain/das/needs.go#L72`

**(3) When the results differ.** After Gloas, the spec's minimum serving window is 14299 epochs, but Prysm keeps serving and retaining 33024. Prysm serves *more* than the minimum, so it does not violate the "must serve" rule. It only differs if a peer relies on the shorter window, for example in its `earliest_available_slot` or status expectations.

**(4) Lighthouse.** L`consensus/types/src/core/chain_spec.rs#L1949` reads `min_epochs_for_block_requests: 33024`. It is also fixed, so Lighthouse matches Prysm, not the spec.

**(5) Confidence: medium.**
- I searched P for any Gloas-specific override: grep for `MinEpochsForBlockRequests` across the repo found only config uses.
- The deviation is real, but it is a superset of what the spec requires.

**(6) Severity:** cosmetic (resource use only).

**Known-check:** **not found.** Queries: "MIN_EPOCHS_FOR_BLOCK_REQUESTS", "min epochs block requests gloas", "8061", "churn gloas". Unrelated retention/pruning hits: #17527 (open), #17675 (open).

### prysm-3. Gloas `compute_weak_subjectivity_period` (EIP-8061)

Prysm, EIP-8061: **affects live code, details withheld.**

For contrast, Lighthouse has a separate Gloas function at L`consensus/types/src/state/beacon_state.rs#L4107-L4125`.

**Known-check:** **not reported in detail** (live code). The query "weak subjectivity gloas" returned 0 results.

*Merge note:* the Lighthouse item's cross-read of Prysm (its §3) also flags this Prysm function as differing from Gloas weak-subjectivity.md and recommends checking it in the Prysm item. Because the Prysm item withholds the details under the live-code rule, this merge does not repeat the Lighthouse item's file and formula description. The two items agree that the function differs.

### teku-1. EIP-8282: non-builder deposits fund a builder at the fork — spec-version lag, known

1. **Spec, verbatim.** [specs/gloas/fork.md#L83-L119](https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/fork.md#L65-L121):
   ```python
   if not is_builder_withdrawal_credential(deposit.withdrawal_credentials):
       pending_deposits.append(deposit)
       continue
   ```
   The gate comes before the builder lookup; only builder-credential deposits may fund builders.
2. **Teku, verbatim.** [GloasStateUpgrade.java#L222-L250](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas/forktransition/GloasStateUpgrade.java#L214-L273) looks up an existing builder first, and that branch contains:
   ```java
   builders.set(
       builderIndex,
       builder.copyWithNewBalance(builder.getBalance().plus(deposit.getAmount())));
   ```
   Credential checking appears only in the missing-builder alternative. This is Gloas-only upgrade logic.
3. **Differing condition, in words.** At upgrade, the pending queue holds a valid builder-credential registration for a new public key, followed by a non-builder-credential deposit for the same key. The key is not already a validator.
   - Both implementations register the first deposit.
   - Teku credits the second deposit to the builder and removes it from the queue.
   - Master leaves the second deposit queued for validator processing.

   The builder balance and the pending-deposit root differ, and so does the state root. Both deposits must still be pending at the fork.
4. **Lighthouse.** [consensus/state_processing/src/upgrade/gloas.rs#L258-L303](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/consensus/state_processing/src/upgrade/gloas.rs#L258-L303) agrees with Teku. Credentials are checked inside the missing-builder arm, while the existing-builder arm executes `builder.balance.safe_add_assign(deposit.amount)?;`. Stable does not disprove the divergence from master.
5. **Confidence / attempted disproof.** **High** for the conditional state-root difference.
   - The post-fork prefix gate is correct, but it cannot repair this separate upgrade path.
   - The reviewer checked iteration order and same-pass builder visibility.
   - Compared with beta.4: the old routing is what beta.4 specifies, so this is version lag, not nonconformance to Teku's declared release target.
   - Spec PR 5736 and Teku PR 11582 describe this exact case.
   - The item makes no claim that any particular public network had such a pending queue.
6. **Severity and known-check.** **Consensus split**, conditional on a qualifying fork queue and on clients targeting different spec versions.
   - **Known:** [Teku PR 11582](https://github.com/Consensys-Incorporated/teku/pull/11582), open/unmerged; [issue 11510](https://github.com/Consensys-Incorporated/teku/issues/11510), closed; [spec PR 5736](https://github.com/ethereum/consensus-specs/pull/5736), merged 2026-10-26.
   - No accessible Hive/EEST test result matching this case was found (see §4).

### teku-2. EIP-8282: validator-key overlap blocks a valid builder registration — spec-version lag, known

1. **Spec, verbatim.** [specs/gloas/fork.md#L65-L69](https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/fork.md#L65-L121): "A valid builder registration can use a validator's public key." At lines 95–119, the validator/pending-validator test is confined to the **invalid-signature** branch; a valid signature reaches `add_builder_to_registry`.
2. **Teku, verbatim.** [GloasStateUpgrade.java#L222-L268](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas/forktransition/GloasStateUpgrade.java#L214-L273):
   ```java
   if (validatorsUtil.getValidatorIndex(state, pubkey).isPresent()) {
     pendingDeposits.add(deposit);
     continue;
   }
   ```
   And, before checking the builder deposit signature:
   ```java
   if (pendingValidators.isPendingValidator(pubkey)) {
     pendingDeposits.add(deposit);
     return;
   }
   ```
3. **Differing condition, in words.** A queued deposit has builder credentials and a valid validator-deposit-domain signature, as fork onboarding requires. No builder yet uses its public key, and the key either already belongs to a validator or has an earlier valid non-builder deposit retained in the queue.
   - Master registers the builder with the fixed payload-builder version and removes this deposit from the queue.
   - Teku keeps the deposit queued and creates no builder.

   The registry, the pending queue and the upgrade root differ.
4. **Lighthouse.** [gloas.rs#L258-L295](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/consensus/state_processing/src/upgrade/gloas.rs#L258-L303) agrees with Teku. Existing validators are skipped at lines 260–263, and `valid_pending_validator_pubkeys.contains(&deposit.pubkey)` retains the deposit at lines 278–280, before the signature check.
5. **Confidence / attempted disproof.** **High.**
   - [The lazy tracker](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas/forktransition/GloasStateUpgrade.java#L97-L317) requires an earlier *valid* validator deposit; an invalid earlier deposit alone does not trigger this.
   - Under master, an invalid builder signature correctly keeps validator top-ups queued, so that is not another finding.
   - Beta.4 specifies the old priority, which explains why both clients agree.
   - Builder requests after the fork do not impose this validator-key veto.
6. **Severity and known-check.** **Consensus split**, conditional, at the upgrade.
   - **Known:** open [Teku PR 11582](https://github.com/Consensys-Incorporated/teku/pull/11582) explicitly covers existing and pending validators; [issue 11510](https://github.com/Consensys-Incorporated/teku/issues/11510) is closed; [spec PR 5736](https://github.com/ethereum/consensus-specs/pull/5736) is merged.
   - The item counts teku-1 and teku-2 as two forms of one known version-lag family, not as two new discoveries.

### teku-3. EIP-7688: removed transaction-count cap retained in Gloas decoder — not found

1. **Spec, verbatim.**
   - [EIP-7688#L81-L86](https://github.com/ethereum/EIPs/blob/d4c575521d99ec881a444ab3af604e684b5f0743/EIPS/eip-7688.md#L58-L92): "The `MAX_TRANSACTIONS_PER_PAYLOAD` (1M) limit is no longer enforced".
   - [beacon-chain.md#L352-L371](https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/beacon-chain.md#L352-L371) declares `Transaction(ProgressiveList[Byte])` and `Transactions(ProgressiveList[Transaction])` without the old bounds.
   - For an envelope whose beacon block has not been seen, [p2p-interface.md#L835-L838](https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/p2p-interface.md#L830-L879) says:
     ```python
     if block_root not in store.blocks:
         raise GossipIgnore("envelope's block has not been seen")
     ```
2. **Teku, verbatim.** The **GLOAS** creator in [SchemaRegistryBuilder.java#L436-L452](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/registry/SchemaRegistryBuilder.java#L424-L452) still supplies the old count bound:
   ```java
   SszProgressiveListSchema.create(
       registry.get(TRANSACTION_SCHEMA),
       SszSchemaHints.sszPackedByteLists(),
       SpecConfigBellatrix.required(specConfig).getMaxTransactionsPerPayload()))
   ```
   How the bound is reached:
   - The [mainnet preset](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/bellatrix.yaml#L12-L17) sets it to 1,048,576.
   - [PackedByteListsUtil.java#L49-L51](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/infrastructure/ssz/src/main/java/tech/pegasys/teku/infrastructure/ssz/schema/impl/PackedByteListsUtil.java#L38-L65) checks `elementsCount > maxElementCount` and throws `SszMaxLengthExceededException`. The [progressive packed decoder](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/infrastructure/ssz/src/main/java/tech/pegasys/teku/infrastructure/ssz/schema/AbstractSszProgressiveListSchema.java#L155-L442) reaches that check.
   - Gossip [decodes before processing](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/topichandlers/Eth2TopicHandler.java#L188-L268). [SSZ errors become decoding exceptions](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/encoding/SszGossipCodec.java#L27-L42), which are mapped to `ValidationResult.Invalid` in the same topic handler.

   The finding is the bound in the Gloas schema.
3. **Differing condition, in words.** A Gloas execution-payload gossip envelope references a block that has not been seen and contains 1,048,577 empty transaction byte strings. Its other variable fields are small or empty, and its fixed fields are encoded correctly.
   - The transaction offset table takes 4,194,308 bytes, which is below the [10,485,760-byte ceiling](https://github.com/ethereum/consensus-specs/blob/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/configs/mainnet.yaml#L58-L172).
   - Empty byte strings are legal opaque SSZ transactions, but they are not valid execution transactions.
   - Teku rejects the envelope while decoding it. The spec's gossip function reaches the unknown-block IGNORE instead.

   What is established is a difference in the **decoding/gossip verdict**, not acceptance of a valid execution block. No packet was constructed or sent.
4. **Lighthouse.** [execution_payload.rs#L19-L30](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/consensus/types/src/execution/execution_payload.rs#L19-L30) uses `ProgressiveVariableList<ProgressiveVariableList<u8>>`.
   - [Cargo.toml](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/Cargo.toml#L290-L295) pins the SSZ dependency. That dependency [defaults the length type to U0 and maps zero to no bound](https://github.com/sigp/ssz_types/blob/8955d22edc7633f26fe3b5ac933facb5121d717a/src/progressive_variable_list.rs#L55-L149).
   - The [unknown-block branch](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/beacon_node/beacon_chain/src/payload_envelope_verification/gossip_verified_envelope.rs#L154-L169) returns `BlockRootUnknown`, which is mapped to [MessageAcceptance::Ignore](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/beacon_node/network/src/network_beacon_processor/gossip_methods.rs#L3913-L3926).

   Lighthouse agrees with the spec on this count rule and on the verdict path.
5. **Confidence / attempted disproof.** **Medium overall; high for the extra bound in the static code.**
   - The reviewer checked the actual packed fast path, and Lighthouse's generic default and error mapping.
   - The offset-only case fits within the byte ceiling.
   - Because its contents are invalid at the EL, this case cannot be treated as a split over a valid block.
   - The old 1-GiB per-transaction cap is also still present in [ProgressiveTransactionSchema.java#L22-L34](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ProgressiveTransactionSchema.java#L22-L34). It exceeds the normal message ceiling and has no demonstrated gossip impact, so it is not promoted to a separate candidate.
   - Not established: a runtime reproduction, reachability with a valid block, or an effect on peer scores.
6. **Severity and known-check.** **Cosmetic** on the given scale: an observable difference in gossip classification and conformance, with no demonstrated state-root or head divergence. Peer scoring was not measured.
   - **Not found** as a bug about the removed transaction cap in the issue/PR queries (§4).
   - The related merged bounds-hardening PRs 11299 and 11297 are implementation history. They are not evidence that this particular extra bound has been reported or fixed.

### teku-4. Restricted entry (EIP-8061)

Teku — EIP-8061 — affects live code, details withheld.

**Known-check:** none reported in the item for this entry.

## 3. Cross-client table

Each cell gives the item's verdict for that client. Lighthouse cells for Prysm-only or Teku-only rows come from the other items' Lighthouse cross-reads (at stable), and vice versa where an item cross-read another client. "not checked" means that no item reports checking that rule for that client.

| Rule (spec at `8a80ffa8…` unless noted) | Lighthouse | Prysm | Teku |
|---|---|---|---|
| Fork onboarding `onboard_builders_from_pending_deposits`, master order (0xB0 gate first; valid builder sig may reuse validator / pending-validator key) | differs (see lighthouse-1); matches beta.4 | differs (see prysm-1); matches beta.4 | differs (see teku-1, teku-2); matches beta.4 |
| Gloas `compute_min_epochs_for_block_requests` (≈14,299 epochs) / envelope serving window | differs (see lighthouse-2) | differs (see prysm-2) | not checked |
| EIP-7688 execution-request per-payload limits (asserted in `apply_parent_execution_payload`; decode-time checks allowed) | differs (see lighthouse-3; only for req/resp envelopes, otherwise agrees) | agrees with spec (rejects at decode, same block-validity result) | agrees with spec |
| EIP-7688 `MAX_TRANSACTIONS_PER_PAYLOAD` no longer enforced on `Transactions` | agrees with spec (teku item cross-read) | not checked | differs (see teku-3) |
| `process_builder_deposit_request`: 0xB0 gate first, for both registration and top-up | agrees with spec | agrees with spec | agrees with spec |
| New builder: `DOMAIN_BUILDER_DEPOSIT` signature for a new key only; `PAYLOAD_BUILDER_VERSION` | agrees with spec (`Builder::from_deposit` body not read) | agrees with spec (request-time `AddBuilderFromDeposit` version unverified) | agrees with spec |
| Top-up of an exited builder resets `withdrawable_epoch` only when not FAR_FUTURE and balance == 0, before crediting | agrees with spec | agrees with spec | agrees with spec |
| `get_index_for_new_builder` reuse rule | agrees with spec | not checked | not checked |
| `process_builder_exit_request` (known, active, source address, no pending withdrawals, `initiate_builder_exit`) | agrees with spec | agrees with spec | agrees with spec |
| `is_active_builder` | agrees with spec | agrees with spec (within the exit path) | agrees with spec |
| `get_pending_balance_to_withdraw_for_builder` (both queues) | agrees with spec | agrees with spec (pending-withdraw == 0 check) | agrees with spec |
| `apply_parent_execution_payload` order: payment settlement, then deposits … builder exits | not checked | agrees with spec | agrees with spec |
| Execution-requests encoding/decoding for types 0x03/0x04 (strict order, skip empty, nonempty data, alignment) | agrees with spec for the encoding; engine-API decoding not verified | agrees with spec (skip-empty encoding not checked line by line) | agrees with spec |
| `ExecutionRequests` as a 5-field ProgressiveContainer / requests root | agrees with spec | agrees with spec | agrees with spec |
| EIP-7916 progressive merkleization (capacities 1, 4, 16, …; mix-ins) | not checked (`ssz_types` not audited) | agrees with spec | agrees with spec |
| EIP-7688 fork upgrade rematerializes progressive list roots | not checked | not checked | agrees with spec |
| EIP-8045 proposer sampling excludes slashed validators | agrees with spec | agrees with spec | agrees with spec |
| EIP-8045 `proposer_lookahead` copied, not recomputed, at the fork | agrees with spec | agrees with spec | agrees with spec |
| EIP-8061 activation churn (capped at 256 ETH, increment rounding) | agrees with spec (`single_pass.rs` call site not checked) | agrees with spec | agrees with spec |
| EIP-8061 exit churn uncapped; `compute_exit_epoch_and_update_churn` | agrees with spec | agrees with spec | agrees with spec |
| EIP-8061 consolidation churn (TAB // 65536) | agrees with spec | agrees with spec | agrees with spec |
| Gloas config/preset constants (32768, 65536, 256e9, 64, 16; 0xB0; 64/16 builder request limits) | agrees with spec | agrees with spec | not checked |
| EIP-8061 Gloas `compute_weak_subjectivity_period` | agrees with spec | differs (see prysm-3; affects live code, details withheld) | agrees with spec (rules table) |
| EIP-8061 live-code entry, rule withheld | none found in shared live branches | see prysm-3 | differs (see teku-4; affects live code, details withheld) |
| Builder gossip validation (p2p builder checks) | not checked | not checked (stated limit) | not checked |

Differences between clients, as the table shows them:
- On fork onboarding, all three clients agree with one another (beta.4) and differ from master.
- On the minimum serving window, Lighthouse and Prysm agree with each other (fixed 33024) and differ from the spec. Teku was not checked.
- On the transaction-count cap, Teku differs from Lighthouse (teku-3). Prysm was not checked.
- On the per-payload request limits, Lighthouse's decode-time enforcement differs from the spec only on the req/resp envelope path (lighthouse-3).
- Weak subjectivity: Prysm differs (withheld); Lighthouse and Teku agree.

## 4. Known-check summary

| Candidate | Status | Basis |
|---|---|---|
| lighthouse-1 | spec-version lag; **not found** in sigp/lighthouse | Spec fix consensus-specs#5736 (merged 2026-10-26); Lighthouse targets beta.4 via #10307 |
| lighthouse-2 | **related known**: sigp/lighthouse#4553 (open, 2023, not Gloas-specific) | queries in lighthouse-2 |
| lighthouse-3 | **known / intentional**: sigp/lighthouse#10106 (merged 2026-09-29) | already indexed |
| prysm-1 | spec-version lag; **not found** in OffchainLabs/prysm | related closed PRs #16796, #17440, #17129 and issue #17147; some queries rate-limited |
| prysm-2 | **not found** | unrelated #17527, #17675 |
| prysm-3 | live code; not reported in detail | "weak subjectivity gloas": 0 results |
| teku-1 | **known**: Teku PR 11582 (open/unmerged), issue 11510 (closed); spec PR 5736 | |
| teku-2 | **known**: same as teku-1 (one version-lag family) | |
| teku-3 | **not found** | PRs 11299/11297 (merged) are related hardening, not this bound |
| teku-4 | live code; no known-check reported | |

The three onboarding candidates (lighthouse-1, prysm-1, teku-1/teku-2) are the same spec-version lag (beta.4 vs master after PR 5736):
- It is known for Teku through PR 11582.
- No Lighthouse or Prysm issue or PR tracking it was found.

Other known context, not candidates:
- Lighthouse: #9602/#9674 (EIP-8045 fork-boundary tests); #10380 (merged 2026-10-27, slashed exclusion in FCR proposer score); #7347 and #9290 (weak subjectivity).
- Prysm: #17412 (ProgressiveList limits on unmarshal, merged, already indexed); #16857 (EIP-8045 implementation, closed PR).
- Teku:
  - 10983 (merged 2026-07-20, extra 7688 validations);
  - 11408 (merged 2026-10-06, payment before requests; the fix is present at the pin);
  - 10766 (proposer changes), 10629 (churn), 10739 (weak subjectivity).
- Teku PR 11582: its author reports that the master-generated fork tests pass on the PR and fail on current Teku master. This is a third-party report, not a test run.

**Hive / execution-spec-tests cross-check:**
- **Lighthouse item:** not checked (time).
- **Prysm item:** does not report one.
- **Teku item:** attempted for teku-1 to teku-3. No test-level result was readable:
  - hive.ethpandaops.io rendered no result rows and returned HTTP 403 on direct retrieval;
  - hive-results.ethdevops.io was inaccessible;
  - execution-specs `tests@v21.0.2` (published 2026-10-26) and the Glamsterdam devnet v8.1.0 tracker execution-specs#3295 hold no Teku-specific results for these CL conditions.

So no candidate is confirmed or disproved by public Hive/EEST results.

**Search limits:**
- GitHub code search returned HTTP 401 without auth (lighthouse item).
- The Prysm searches partly hit the unauthenticated rate limit.
- For Teku's broad `gloas` query (574 hits), only the first 100 were inspected, supplemented by narrower searches.
