# ePBS Testing Scenarios (EIP-7732 / Gloas)
Aligned with `consensus-specs` **v1.7.0-alpha.5** (Gloas, published 2026-04-19).
## Slot Timeline Reference
Deadlines in Gloas are basis points of `SLOT_DURATION_MS`, not hard-coded seconds. On mainnet config (`SECONDS_PER_SLOT=12`):
- **t=0s**: Proposer broadcasts beacon block containing `SignedExecutionPayloadBid`
- **t=3s** (`ATTESTATION_DUE_BPS_GLOAS=2500`, 25%): Attestation deadline — validators attest to the consensus block and signal observed payload status via `AttestationData.index` (`0`=EMPTY, `1`=FULL for past-slot votes). Execution validation is NOT required to attest.
- **t≤9s**: Builder broadcasts `SignedExecutionPayloadEnvelope` on the `execution_payload` gossip topic. No spec-mandated reveal deadline separate from the PTC vote — earlier reveals just give more PTC members time to observe it.
- **t=9s** (`PAYLOAD_ATTESTATION_DUE_BPS=7500`, 75%): PTC attestation deadline — PTC members broadcast `PayloadAttestationMessage` carrying TWO booleans: `payload_present` (envelope observed) and `blob_data_available` (blobs observed).
**Payload status** (fork-choice.md):
- `PAYLOAD_STATUS_PENDING` — block seen, envelope not yet observed (transient)
- `PAYLOAD_STATUS_FULL` — envelope observed with matching `block_hash`
- `PAYLOAD_STATUS_EMPTY` — envelope not observed, or parent was EMPTY
**Slot outcomes:** FULL (beacon block + payload revealed + PTC quorum), EMPTY (beacon block, no payload), SKIPPED (no beacon block). SKIPPED is a consensus outcome distinct from `PAYLOAD_STATUS_EMPTY`.
---
## 1. Happy Path
### 1.1 Basic Block Production & ePBS Flow
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-1 | Builder submits valid bid, proposer includes it, builder reveals on time, PTC votes PAYLOAD_PRESENT | State transitions correctly, proposer receives payment, execution payload processed, slot is FULL | :x: |
| HP-2 | Multiple consecutive FULL slots across an epoch | Epoch processing succeeds, builder pending payments roll to pending withdrawals, finality achieved | :x: |
| HP-3 | Builder bid with maximum blob commitments (`get_blob_parameters(epoch).max_blobs_per_block`; Electra default `MAX_BLOBS_PER_BLOCK_ELECTRA=9`) | All blob KZG commitments validated, `blob_data_available` vote passes quorum, PTC attests correctly | :x: |
| HP-4 | Builder bid with zero blobs | Valid bid, FULL slot, no blob availability issues | :x: |
| HP-5 | Multiple builders submit competing bids for the same slot | Proposer selects one, only selected builder's payment deducted, losing builders unchanged | :x: |
| HP-6 | Proposer self-builds using `BUILDER_INDEX_SELF_BUILD` (`BuilderIndex(UINT64_MAX)` sentinel) | Valid block produced, no builder signature required, no builder balance deducted | :x: |
| HP-7 | Proposer receives payment from builder upon beacon block inclusion (not upon reveal) | Payment recorded unconditionally in `builder_pending_payments` at `process_execution_payload_bid`; settlement is conditional (see HP-8) | :x: |
| HP-8 | Builder pending payment settles only if attestation-weighted quorum ≥ `get_builder_payment_quorum_threshold(state)` (`BUILDER_PAYMENT_THRESHOLD_NUMERATOR/DENOMINATOR` = 6/10 of per-slot active balance) | `process_builder_pending_payments` at epoch boundary rolls only payments meeting quorum into `builder_pending_withdrawals`; under-quorum payments dropped | :x: |
### 1.2 Validator Operations
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-9 | Validator submits attestation for a FULL slot | Attestation processed, fork choice weight updated, participation tracked | :x: |
| HP-10 | Validator submits attestation for an EMPTY slot (beacon block present, no payload) | Attestation valid for consensus block, processed normally | :x: |
| HP-11 | Sync committee member produces `SyncAggregate` contribution | Sync aggregate included in next block, light client protocol functions | :x: |
| HP-12 | Proposer slashing — valid evidence of proposer equivocation submitted | Slashing processed, offending validator penalized and force-exited, whistleblower rewarded | :x: |
| HP-13 | Attester slashing — valid evidence of attester equivocation submitted | Slashing processed, offending validators penalized, whistleblower rewarded | :x: |
| HP-14 | Validator submits `SignedVoluntaryExit` | Exit initiated, validator enters exit queue, withdrawable after exit period | :x: |
| HP-15 | Validator `BLSToExecutionChange` — switch withdrawal credentials from BLS (`0x00`) to execution address (`0x01`) | Withdrawal credentials updated, validator eligible for automatic withdrawals | :x: |
| HP-16 | Validator full withdrawal — exited validator's balance swept to execution address | Balance withdrawn via execution payload withdrawals in a FULL slot | :x: |
| HP-17 | Validator partial withdrawal — excess balance above `MAX_EFFECTIVE_BALANCE` auto-swept | Partial withdrawal included in execution payload, balance reduced to max effective | :x: |
| HP-18 | EL-triggered voluntary exit via `WithdrawalRequest` (EIP-7002) with `amount=0` | Validator exit initiated from execution layer, no BLS key needed | :x: |
| HP-19 | EL-triggered partial withdrawal via `WithdrawalRequest` (EIP-7002) with `amount>0` | Partial withdrawal processed, specified amount queued for withdrawal | :x: |
### 1.3 Deposit Operations
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-20 | Legacy Eth1 deposit — new validator deposit via deposit contract Merkle proof | Deposit processed, validator added to `pending_deposits`, activated after eligibility delay | :x: |
| HP-21 | EL-triggered deposit via `DepositRequest` (EIP-6110) — new validator | Deposit request in execution payload processed, validator queued for activation | :x: |
| HP-22 | EL-triggered top-up deposit for existing validator | Validator balance incremented, effective balance updated at epoch boundary | :x: |
| HP-23 | Validator consolidation via `ConsolidationRequest` (EIP-7251) — merge two validators into one | Source validator exits, balance transferred to target, target effective balance increases (up to 2048 ETH) | :x: |
### 1.4 Builder Lifecycle Operations
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-24 | Builder registration — deposit with `BUILDER_WITHDRAWAL_PREFIX` (`0x03`) withdrawal credentials | Builder added to `state.builders` registry, assigned builder index encoded as `builder_index \| BUILDER_INDEX_FLAG` (`2**40`), `is_builder_index()` returns true, can submit bids | :x: |
| HP-25 | Builder top-up deposit — additional deposit to existing builder pubkey | Builder balance incremented, can cover larger bid values | :x: |
| HP-26 | Builder voluntary exit via `SignedVoluntaryExit` with builder-encoded validator index (`is_builder_index()` true) | `withdrawable_epoch` set to `current_epoch + MIN_BUILDER_WITHDRAWABILITY_DELAY` (64 epochs) | :x: |
| HP-27 | Builder exit via EL-triggered `WithdrawalRequest` from builder's execution address | Builder exit initiated from execution layer | :x: |
| HP-28 | Builder withdrawal — exited builder reaches `withdrawable_epoch`, balance swept | Builder balance returned to `execution_address`, index freed for reuse by a future builder registration | :x: |
| HP-29 | Builder bid payment settlement — pending payment meets quorum (see HP-8), rolls to `builder_pending_withdrawals` | Payment deducted from builder balance, queued for execution-layer withdrawal to proposer's address | :x: |
| HP-30 | Builder bid payment dropped — PTC + attestation quorum insufficient | Payment entry in `builder_pending_payments` dropped at epoch boundary, builder balance NOT deducted (contrast: bid consumed if block made it on chain, payment tracking handles settlement) | :x: |
| HP-31 | Multiple builders register across several epochs | Builder indices assigned correctly, all builders can submit bids independently | :x: |
| HP-32 | Builders do NOT perform validator duties (no attestation, sync, or proposer rewards) | Verified: no rewards/penalties applied to builder indices in `process_rewards_and_penalties` | :x: |
### 1.5 PTC Duties
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-33 | PTC member produces and broadcasts `PayloadAttestationMessage` with `payload_present=True, blob_data_available=True` for timely reveal | Message accepted on `payload_attestation_message` gossip topic, included in next block's `payload_attestations` | :x: |
| HP-34 | Proposer includes aggregated PTC attestations from previous slot in beacon block | Up to `MAX_PAYLOAD_ATTESTATIONS=4` aggregates included, all validated | :x: |
| HP-35 | PTC member broadcasts `payload_present=True, blob_data_available=False` (payload seen but blobs missing) | Fork choice counts `payload_timeliness_vote` but NOT `payload_data_availability_vote` toward thresholds | :x: |
### 1.6 Epoch Boundary Processing
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| HP-36 | Epoch transition with mix of FULL and EMPTY slots | Justification/finalization computed, pending payments settled per quorum, rewards/penalties applied | :x: |
| HP-37 | Epoch transition processes `builder_pending_payments` → `builder_pending_withdrawals` via `process_builder_pending_payments` | Only entries meeting quorum rolled over (6/10 active balance per slot); under-quorum entries dropped; vector length = `2 * SLOTS_PER_EPOCH` | :x: |
| HP-38 | Epoch transition applies validator rewards and penalties | Attestation rewards, sync committee rewards, proposer rewards all computed correctly | :x: |
| HP-39 | Epoch transition processes pending validator deposits and activations | Validators in activation queue activated up to churn limit | :x: |
| HP-40 | Epoch transition processes validator exits and ejections | Validators below ejection balance force-exited, voluntary exits finalized | :x: |
| HP-41 | Epoch transition updates effective balances | Effective balances adjusted based on actual balances with hysteresis | :x: |
| HP-42 | Epoch transition shuffles committees and assigns new PTC | New PTC committee selected for next epoch's slots via RANDAO-based shuffling | :x: |
---
## 2. Builder Behavior
### 2.1 Honest Builder
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| BH-1 | Builder reveals payload at t=2s (well before PTC deadline) | PTC unanimously votes `payload_present=True, blob_data_available=True`, fork choice assigns `PAYLOAD_STATUS_FULL` via `get_payload_status_tiebreaker` | :x: |
| BH-2 | Builder reveals payload at t=8.9s (just before PTC deadline at 7500 BPS) | Some PTC members may not observe it in time, mixed votes, fork choice outcome depends on whether `payload_timeliness_vote` sum exceeds `PAYLOAD_TIMELY_THRESHOLD` (`PTC_SIZE // 2 = 256`) | :x: |
| BH-3 | Builder reveals payload with valid state root matching post-state | State root commitment in envelope matches computed state | :x: |
### 2.2 Dishonest Builder — Withholding
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| BD-1 | Builder wins bid, never reveals payload | PTC votes `payload_present=False`, block becomes `PAYLOAD_STATUS_EMPTY`, payment recorded in `builder_pending_payments` but fails to reach quorum → dropped at epoch boundary | :x: |
| BD-2 | Builder reveals payload after t=9s PTC deadline | Envelope ignored by PTC members, `payload_present=False`, block EMPTY | :x: |
| BD-3 | Builder withholds payload at epoch boundary slot | Epoch processing handles EMPTY final slot, `process_builder_pending_payments` settles only quorum-meeting payments | :x: |
### 2.3 Dishonest Builder — Equivocation
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| BD-4 | Builder sends two different `SignedExecutionPayloadEnvelope` for same slot | Only one accepted, gossip validation rejects duplicates | :x: |
| BD-5 | Builder sends payload with incorrect block hash (doesn't match bid) | Rejected by gossip validation, slot EMPTY | :x: |
| BD-6 | Builder sends payload with incorrect `beacon_block_root` | Rejected, does not match beacon block containing bid | :x: |
| BD-7 | Builder sends payload with incorrect `state_root` | State root mismatch detected during async execution validation | :x: |
| BD-8 | Builder submits bid with value > balance | Bid rejected during `process_execution_payload_bid` | :x: |
| BD-9 | Builder reveals payload to only a subset of PTC members (selective reveal) | Mixed PTC votes, threshold determines FULL or EMPTY, fork choice handles split view | :x: |
### 2.4 Free Option Problem
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| BD-10 | Builder commits at t=0, monitors market, withholds if unfavorable | EMPTY slot if withheld. Payment is recorded in `builder_pending_payments` but will be dropped at epoch boundary unless quorum reached; net cost to builder is opportunity cost plus any reputational/peer-scoring fallout | :x: |
| BD-11 | Builder exercises free option during high-volatility period (multiple consecutive withholdings) | Network liveness maintained, slot production continues, all withheld payments dropped at epoch boundary | :x: |
| BD-12 | Builder reveals payload body but withholds blobs | `payload_present=True` votes pass timeliness threshold, but `blob_data_available=False` fails `DATA_AVAILABILITY_TIMELY_THRESHOLD`; fork choice treats as unavailable / EMPTY | :x: |
---
## 3. Proposer Behavior
### 3.1 Honest Proposer
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PH-1 | Proposer selects highest-value valid bid from available builders | Correct bid selected, payment matches bid value | :x: |
| PH-2 | Proposer includes PTC attestations from previous slot in beacon block body | `MAX_PAYLOAD_ATTESTATIONS` (4) limit respected, attestations validated | :x: |
### 3.2 Dishonest Proposer
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PD-1 | Proposer includes invalid builder bid (invalid signature) | Block rejected by other validators | :x: |
| PD-2 | Proposer includes bid from builder with insufficient balance | Block rejected during state transition | :x: |
| PD-3 | Proposer delays beacon block past `ATTESTATION_DUE_BPS_GLOAS` (t≈3s on mainnet config) — timing game | Reduced attestation participation, possible SKIPPED from some validators' perspective | :x: |
| PD-4 | Proposer includes stale PTC attestations from wrong slot | Attestation slot validation fails, block rejected | :x: |
| PD-5 | Proposer includes more than `MAX_PAYLOAD_ATTESTATIONS=4` | Block rejected | :x: |
| PD-6 | Proposer withholds beacon block entirely | Slot SKIPPED, next proposer builds on previous head | :x: |
| PD-7 | Proposer slashed — pending builder payment from proposer's slot is cleared | `slash_validator` zeroes out corresponding entry in `builder_pending_payments`; builder effectively refunded for that bid | :x: |
---
## 4. PTC Voting
### 4.1 Normal Operation
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PTC-1 | All 512 PTC members vote `payload_present=True, blob_data_available=True` for timely reveal | Both `payload_timeliness_vote` and `payload_data_availability_vote` exceed their thresholds; fork choice assigns `PAYLOAD_STATUS_FULL` | :x: |
| PTC-2 | All 512 PTC members vote `payload_present=False` for withheld payload | `payload_timeliness_vote` sum is 0; block becomes `PAYLOAD_STATUS_EMPTY`; `get_payload_status_tiebreaker` may prefer sibling FULL branch | :x: |
| PTC-3 | Exactly `PAYLOAD_TIMELY_THRESHOLD + 1 = 257` members vote `payload_present=True` (threshold is strictly `sum > PTC_SIZE // 2`) | Threshold exceeded, FULL | :x: |
| PTC-4 | Exactly `PAYLOAD_TIMELY_THRESHOLD = 256` or fewer vote `payload_present=True` (threshold is strict `>`, so 256 is the boundary failure case including the 256/256 split) | Threshold NOT exceeded, block treated as `PAYLOAD_STATUS_EMPTY` despite some PTC members seeing payload | :x: |
| PTC-5 | 300 vote `payload_present=True` but only 200 vote `blob_data_available=True` | `payload_timeliness_vote` passes, `payload_data_availability_vote` fails `DATA_AVAILABILITY_TIMELY_THRESHOLD=256`; fork choice treats block as unavailable | :x: |
### 4.2 Split Views
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PTC-6 | PTC members in different regions see payload at different times | Members receiving before deadline vote True, others False; outcome depends on global sum | :x: |
| PTC-7 | Some PTC members offline (e.g., 100 of 512) — absent votes count as False in bitvector | Threshold still measured over `PTC_SIZE=512`, not over respondents; more non-voters → harder to reach FULL | :x: |
| PTC-8 | All PTC members offline or fail to vote | `payload_timeliness_vote` sum = 0, block becomes `PAYLOAD_STATUS_EMPTY`, chain continues | :x: |
### 4.3 Equivocation & Attacks
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PTC-9 | PTC member sends two `PayloadAttestationMessage` with different `payload_present` for same slot | Gossip validation / `process_payload_attestation_message` handles duplicates; last write in `payload_timeliness_vote[ptc_index]` wins per spec, no crash | :x: |
| PTC-10 | PTC member votes `payload_present=True` without actually receiving payload | Protocol can't directly enforce; fork choice weight reflects aggregate PTC outcome; malicious votes contribute to quorum but are rare with balance-weighted selection | :x: |
| PTC-11 | Attacker controls minority of PTC, attempts to flip outcome | PTC is selected via balance-weighted sampling from all beacon committees for the slot (`get_ptc`); manipulation requires large stake share | :x: |
| PTC-12 | PTC member sends attestation for wrong `beacon_block_root` | Rejected by gossip validation | :x: |
| PTC-13 | PTC member sends attestation for future slot | Rejected, slot validation fails | :x: |
### 4.4 Aggregation
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| PTC-14 | Proposer at N+1 aggregates PTC attestations from slot N into `payload_attestations` | Correct aggregation, fits within `MAX_PAYLOAD_ATTESTATIONS` (4) | :x: |
| PTC-15 | PTC attestations arrive late (after N+1 proposer builds block) | Late attestations not included in that block, handled by fork choice directly | :x: |
---
## 5. Fork Choice
### 5.1 Payload Status Lifecycle
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| FC-1 | Two competing tips: `PAYLOAD_STATUS_FULL` slot vs `PAYLOAD_STATUS_EMPTY` slot at same height | `get_payload_status_tiebreaker` prefers FULL; when combined with LMD-GHOST weight, FULL wins assuming honest majority PTC | :x: |
| FC-2 | Chain tip has `PAYLOAD_STATUS_PENDING` (block seen but envelope not yet) | `ForkChoiceNode(root, PAYLOAD_STATUS_PENDING)` present in child set; attesters can still attest to consensus block (payload status signaled via `AttestationData.index` in next slot's attestations) | :x: |
| FC-3 | Fork: 3 consecutive EMPTY slots vs 2 FULL slots | Fork choice weight correctly computed; branch with more cumulative `is_payload_present` evidence wins at ties; LMD-GHOST attestation weight dominates otherwise | :x: |
| FC-4 | Block transitions from `PAYLOAD_STATUS_FULL` to invalid via async EL validation | `notify_new_payload` returns INVALID after block was treated as FULL; store updates, canonical chain reorgs to branch without invalid payload | :x: |
| FC-5 | `get_parent_payload_status` returns EMPTY for a block whose parent's envelope was never observed | Child block's bid commitment cannot be applied; `apply_parent_execution_payload` skipped per alpha-5 `process_parent_execution_payload` logic | :x: |
### 5.2 Payload Status Tiebreaker (replaces "boost" framing)
Alpha-5 removed the `BUILDER_REVEAL_BOOST` concept. Fork choice head selection uses weight + `get_payload_status_tiebreaker(store, node)` (fork-choice.md §`get_payload_status_tiebreaker`). `PROPOSER_SCORE_BOOST=40` still applies for the current-slot proposer, orthogonal to payload status.
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| FC-6 | PTC quorum reached for block N → descendant nodes of N keep `PAYLOAD_STATUS_FULL`; if PTC flips later, descendants re-resolve | Store's `payload_timeliness_vote` updates drive payload_status on subsequent `get_head` calls — no persistent "boost timer" | :x: |
| FC-7 | Competing blocks at same height: one FULL, one EMPTY; neither has PTC majority yet | Both remain PENDING in `ForkChoiceNode`; head selection falls back to LMD-GHOST weight + proposer boost until PTC quorum resolves | :x: |
| FC-8 | Proposer boost at slot N+1 applied while slot N is still PENDING | `PROPOSER_SCORE_BOOST` adds weight to N+1's branch independently of N's payload status resolution | :x: |
| FC-9 | `should_apply_proposer_boost` conditional logic — parent is weak and has early equivocations | Boost application skipped per alpha-5 fork-choice.md rules; verify with targeted equivocation testcase | :x: |
### 5.3 Deferred Execution
Alpha-5 processes the *parent's* execution payload during the current block's processing (`process_parent_execution_payload` in beacon-chain.md), not the current block's. The envelope for block N is verified when it arrives (`on_execution_payload_envelope`), but its state application is deferred to N+1.
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| FC-10 | Execution payload received, state transition at N+1 returns INVALID via EL | Fork choice transitions N from FULL to invalid, N+1 and descendants reorged | :x: |
---
## 6. Network Partition & Latency
### 6.1 Builder Partitioned
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| NP-1 | Builder partitioned from PTC only (reaches some validators) | PTC votes False since they did not observe the envelope; block EMPTY | :x: |
| NP-2 | Builder reconnects and reveals after PTC deadline but before next slot | Late reveal ignored by PTC, block remains EMPTY | :x: |
### 6.2 Proposer Partitioned
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| NP-3 | Proposer partitioned, beacon block not seen by attesters by `get_attestation_due_ms()` (t≈3s mainnet) | No attestations, slot effectively SKIPPED | :x: |
| NP-4 | Proposer's beacon block arrives late (t≈2.9s) due to latency | Reduced attestation weight, competing chain head may emerge | :x: |
### 6.3 General Partitions
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| NP-5 | Network splits 50/50 for one epoch | Neither side finalizes, chain heals on resolution, finality resumes in 2-3 epochs | :x: |
| NP-6 | One side has builder+proposer, other has majority attesters | Majority side treats slot as SKIPPED | :x: |
| NP-7 | Asymmetric partition: payload reaches ~307/512 (60%) of PTC, 205 (40%) do not | 307 > 256 so `payload_timeliness_vote` passes threshold; block treated FULL | :x: |
### 6.4 High Latency
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| NP-8 | Global latency spike (all messages delayed 2-3 seconds) | Deadlines still met with reduced margins, some missed attestations, chain continues | :x: |
| NP-9 | Latency spike at builder reveal time (payload arrives at PTC at deadline) | Borderline PTC votes, fork choice must converge | :x: |
| NP-10 | Latency between CL and EL on same node (Engine API delayed) | Deferred execution tolerates this, PTC attestation unaffected by EL slowness | :x: |
---
## 7. Reorg Scenarios
### 7.1 Single-Slot Reorgs
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| RO-1 | Slot N block arrives late, slot N+1 proposer builds on N-1 | If slot N has sufficient LMD-GHOST weight plus payload-status tiebreaker advantage, it reorgs back in | :x: |
| RO-2 | Builder reveals at N, attacker proposer N+1 builds on `PAYLOAD_STATUS_EMPTY` version of N | Fork choice picks FULL branch via `get_payload_status_tiebreaker` when PTC quorum reached; attacker's block is orphaned (see RO-9 for the positive test) | :x: |
| RO-3 | Proposer equivocation at slot N (two beacon blocks with different builder bids) | Fork choice selects one deterministically, slashing evidence generated via `process_proposer_slashing` | :x: |
### 7.2 Multi-Slot Reorgs
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| RO-4 | Three EMPTY slots, then FULL slot on alternate branch from 3 back | Chain follows branch with more cumulative weight | :x: |
| RO-5 | Reorg across epoch boundary (last slot of K, first of K+1) | Epoch processing (pending payment rollover) re-executed on new canonical chain | :x: |
### 7.3 Reorg Impact on Builder Payments
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| RO-6 | Builder's FULL slot gets reorged out | On new canonical chain, `builder_pending_payments` entry for that slot is absent; no withdrawal queued | :x: |
| RO-7 | Builder's bid included in beacon block on orphaned branch | Orphaned branch's `builder_pending_payments` entries are not part of canonical state; builder balance unchanged on canonical chain | :x: |
| RO-8 | Builder reveals, PTC reaches quorum with `payload_present=True`, attacker still manages to reorg slot to EMPTY | `get_payload_status_tiebreaker` should prevent this given honest PTC majority; quantifying the adversarial-stake threshold required is an explicit test target (no fixed "20%" bound in the spec) | :x: |
| RO-9 | **(potuz short-term test)** Attacker N+1 block builds on `PAYLOAD_STATUS_EMPTY` version of slot N while honest chain has N as FULL via PTC quorum — verify attacker's block is orphaned | `get_payload_status_tiebreaker` favors FULL branch; honest N+2 extends canonical FULL chain; attacker N+1 is NOT the head and has no descendants on canonical | :x: |
---
## 8. Slot & Epoch Boundary Edge Cases
### 8.1 Timing
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| SB-1 | Builder reveals at exactly `get_payload_attestation_due_ms()` (t=9s mainnet) | Deterministic handling — PTC members treat as "not observed" per `< deadline` semantics; no ambiguity | :x: |
| SB-2 | PTC member broadcasts `PayloadAttestationMessage` at exactly `get_payload_attestation_due_ms()` | Accepted if within client-side tolerance | :x: |
| SB-3 | Beacon block arrives at t=2.99s (just before `get_attestation_due_ms()`=3s mainnet) | Attesters processing in time attest, others do not | :x: |
| SB-4 | Clock skew between nodes (500ms ahead/behind) | Chain converges despite minor differences | :x: |
### 8.2 Epoch Boundaries
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| SB-5 | EMPTY slot at last slot of epoch (slot 31) | `process_builder_pending_payments` handles correctly, withdrawals not processed | :x: |
| SB-6 | EMPTY slot at first slot of epoch (slot 0) | New PTC committee from new shuffling, builder balance changes finalized | :x: |
| SB-7 | All 32 slots in an epoch are EMPTY | No execution state transitions, consensus continues, withdrawal processing paused | :x: |
| SB-8 | Builder's withdrawal epoch arrives mid-epoch | Builder excluded from new bids, existing pending payments still processed | :x: |
### 8.3 Genesis / First Slot
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| SB-9 | No builders registered at fork activation | Proposers can still produce valid blocks without bids | :x: |
| SB-10 | Builder registers in same epoch as fork activation | Builder immediately available after deposit processing | :x: |
---
## 9. Transition / Upgrade
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| TR-1 | Clean fork transition from pre-Gloas to Gloas at designated epoch | All new state fields initialized, `execution_payload` and `payload_attestation_message` gossip topics activated | :x: |
| TR-2 | Fork transition with pending execution payload in last pre-fork slot | Last pre-fork slot uses old processing, first post-fork uses builder bid via `process_execution_payload_bid` | :x: |
| TR-3 | Fork transition during missed/skipped slots | Fork activation proceeds correctly even with missed slots around fork epoch | :x: |
| TR-4 | Node restart during fork activation epoch | Node identifies correct fork rules based on slot, resumes participation | :x: |
| TR-5 | Beacon state migration — new fields initialized (`builders`, `builder_pending_payments`, `builder_pending_withdrawals`, `payload_expected_withdrawals`) | Hash tree root changes correctly at fork boundary | :x: |
| TR-6 | Checkpoint sync after fork activation | Node loads Gloas state, PTC duties assigned, can process bids | :x: |
| TR-7 | Historical state queries for pre-fork blocks | Pre-fork blocks queryable, old format, no Gloas fields expected | :x: |
| TR-8 | Emergency rollback — Gloas fork fails, nodes fall back | Nodes restart with pre-fork config, chain rolls back to last pre-fork finalized state | :x: |
---
## 9a. Sync (Short-term Test Targets)
Explicit sync edge cases called out by the spec team for devnet testing. These exercise divergence between block-level canonical state and payload-level fork-choice state.
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| SY-1 | **(potuz short-term test)** Range sync across a block whose payload was reorged post-inclusion (block is canonical, payload is `PAYLOAD_STATUS_EMPTY` on fork-choice) | Syncing node converges to the same payload status as peers; does NOT replay the reorged execution payload; `payload_expected_withdrawals` tracking consistent with peers; subsequent FULL slot correctly applies accumulated withdrawals | :x: |
| SY-2 | **(potuz short-term test)** Checkpoint sync seeded at slot 31 FULL when slot 0 of the checkpoint epoch was SKIPPED | Node derives correct PTC assignment for next epoch, `builder_pending_payments` vector (length `2 * SLOTS_PER_EPOCH`) reconstructed consistently, produces valid blocks from first assigned duty | :x: |
| SY-3 | **(potuz short-term test)** Checkpoint sync seeded at slot 31 EMPTY when slot 0 of the checkpoint epoch was SKIPPED | Same invariants as SY-2; verify that EMPTY-at-epoch-boundary checkpoint correctly records un-settled payments and resumes with correct `latest_execution_payload_bid` state | :x: |
| SY-4 | Range sync across the Gloas fork boundary (pre→post fork blocks) | Node correctly applies pre-Gloas state transition for pre-fork slots, Gloas state transition for post-fork slots; no mismatched state root | :x: |
---
## 10. Client Diversity
### 10.1 Multi-Client Interop
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| CD-1 | Mixed CL clients (Prysm, Lodestar, Lighthouse, Teku, Grandine, Nimbus) all producing FULL slots | All clients agree on state root, PTC attestations compatible, finality achieved | :x: |
| CD-2 | Each CL client paired with each EL client (Geth, Nethermind, Besu, Erigon, Reth, EthereumJS) | Engine API changes work across all pairs, deferred execution handled correctly | :x: |
| CD-3 | Different clients handle PTC vote aggregation and gossip differently | All clients converge on same fork choice head despite implementation differences | :x: |
| CD-4 | PTC members running different CL clients vote on same payload | All `PayloadAttestationMessage` objects valid and accepted across clients | :x: |
### 10.2 Performance Under Load
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| CD-5 | All clients produce FULL blocks at max gas limit with max blob count | All validate within deferred execution window, no client consistently behind | :x: |
| CD-6 | Stress test PTC gossip: 512 members broadcast `PayloadAttestationMessage` simultaneously | Gossip layer handles burst, messages arrive before aggregation deadline | :x: |
---
## 11. MEV-Related
### 11.1 Builder Market Dynamics
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| MEV-1 | Single builder dominates all slots in an epoch | Protocol functions correctly, no special treatment | :x: |
| MEV-2 | Builder submits bid with value=0 | Valid bid, proposer receives nothing, builder builds the block | :x: |
| MEV-3 | No builders submit bids for a slot | Proposer can self-build or produce empty beacon block | :x: |
### 11.2 MEV and Withholding
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| MEV-4 | Builder discovers higher MEV after committing bid | Cannot change committed block hash, must reveal or withhold | :x: |
### 11.3 Proposer Timing Games
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| MEV-5 | Proposer delays beacon block toward `get_attestation_due_ms()` (t≈3s mainnet) for later/higher bids | Reduced attestation participation, diminished proposer reward | :x: |
| MEV-6 | Builder subsidizes bids (bids more than block is worth) | Protocol processes overpaid bids correctly | :x: |
---
## 12. Gossip & P2P Layer
### 12.1 Topic Validation
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| P2P-1 | `SignedExecutionPayloadBid` on gossip | Only valid bids propagated, invalid rejected | :x: |
| P2P-2 | `SignedExecutionPayloadEnvelope` on gossip | Only envelopes matching committed bid propagated | :x: |
| P2P-3 | `PayloadAttestationMessage` on gossip | Only from actual PTC members for correct slot/root | :x: |
| P2P-4 | Duplicate gossip messages | Deduplication, no double processing | :x: |
| P2P-5 | Malformed gossip messages (corrupted SSZ) | Rejected at gossip validation layer | :x: |
### 12.2 Ordering & Timing
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| P2P-6 | `SignedExecutionPayloadEnvelope` arrives before beacon block | Envelope buffered, processed after block arrives | :x: |
| P2P-7 | `PayloadAttestationMessage` arrives before beacon block | Buffered or rejected per spec, no crash | :x: |
### 12.3 DoS & Spam
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| P2P-8 | Attacker floods network with invalid bids | Gossip validation rejects quickly, no impact on legitimate processing | :x: |
| P2P-9 | Attacker sends `PayloadAttestationMessage` from non-PTC validators | PTC membership checked, messages rejected, peer scored down | :x: |
---
## 13. Withdrawals & Deposits
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| WD-1 | Validator withdrawals processed in FULL slot | Withdrawals computed, deducted from beacon state, credited via execution payload | :x: |
| WD-2 | Validator withdrawals in EMPTY slot (builder withholds) | Withdrawals NOT applied; `payload_expected_withdrawals` field retains the expected list until next FULL slot | :x: |
| WD-3 | Multiple consecutive EMPTY slots then FULL slot | Accumulated `payload_expected_withdrawals` applied on first FULL slot, no withdrawals lost | :x: |
| WD-4 | Max withdrawal queue at epoch boundary with EMPTY slot | Withdrawal processing deferred correctly, no overflow | :x: |
| WD-5 | Builder initiates exit with pending payments still in 2-epoch `builder_pending_payments` window | Pending payments continue to finalize per quorum rules even after exit; balance decremented when entries roll to `builder_pending_withdrawals` | :x: |
| WD-6 | Builder balance goes to exactly 0 after bid payment settles | Builder can't submit new bids with non-zero value | :x: |
| WD-7 | Builder attempts bid with `value > builder.balance` | Bid rejected in `process_execution_payload_bid` | :x: |
---
## 14. Finality & Justification
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| FN-1 | Normal finality: 2 epochs with >2/3 participation, all FULL | Checkpoint justified and finalized as expected | :x: |
| FN-2 | Finality with scattered EMPTY slots | Finality still achievable (EMPTY slots have beacon blocks with attestation weight) | :x: |
| FN-3 | Finality delay due to many EMPTY slots | Finality resumes when FULL slots return | :x: |
| FN-4 | Inactivity leak during prolonged EMPTY slot period | Inactivity scores increase for non-attesting validators, builder behavior doesn't trigger leak for attesters | :x: |
---
## 15. Recovery & Misc Edge Cases
| ID | Scenario | Verify | Status |
|----|----------|--------|--------|
| EC-1 | Builder bid with maximum possible value (entire balance) | Payment processed, balance goes to 0 | :x: |
| EC-2 | Very large execution payload at gas limit with max blobs | Propagation, validation, PTC attestation all complete within deadlines | :x: |
| EC-3 | Zero-transaction execution payload (empty block) | Valid, FULL slot, builder still pays bid value | :x: |
| EC-4 | Builder registry reaches high count (thousands of builders) | No performance degradation in bid processing | :x: |
| EC-5 | PTC committee members are also the proposer for same or next slot | Dual duties handled correctly | :x: |
| EC-6 | Same validator is PTC member and attester in same slot | Both duties performed, no interference | :x: |
| EC-7 | CL node restarts mid-slot during PTC voting phase | Node recovers, doesn't vote twice, resumes duties next slot | :x: |
| EC-8 | EL node crashes during deferred execution | CL continues consensus, EL recovers and processes retroactively | :x: |
| EC-9 | Full node restart (CL + EL) after missing several slots | Node syncs forward, processes FULL and EMPTY slots correctly | :x: |
---
## Summary
| Category | Count |
|----------|-------|
| Happy Path (HP-1..HP-42) | 42 |
| Builder Behavior (BH, BD) | 15 |
| Proposer Behavior (PH, PD) | 9 |
| PTC Voting (PTC-1..15) | 15 |
| Fork Choice (FC-1..10) | 10 |
| Network Partition & Latency (NP-1..10) | 10 |
| Reorg Scenarios (RO-1..9) | 9 |
| Slot & Epoch Boundaries (SB-1..10) | 10 |
| Transition / Upgrade (TR-1..8) | 8 |
| **Sync — short-term test targets (SY-1..4)** | **4** |
| Client Diversity (CD-1..6) | 6 |
| MEV-Related (MEV-1..6) | 6 |
| Gossip & P2P (P2P-1..9) | 9 |
| Withdrawals & Deposits (WD-1..7) | 7 |
| Finality & Justification (FN-1..4) | 4 |
| Recovery & Misc (EC-1..9) | 9 |
| **Total** | **173** |