# `process_proposer_slashing` Test Specification
*Note*: This report is AI-generated and is not an authoritative source of truth.
## Function Signature
```python
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None
```
Location:
[specs/gloas/beacon-chain.md](../../../../../../../specs/gloas/beacon-chain.md)
______________________________________________________________________
## Input Space (State and Operation Fields Read)
### `ProposerSlashing` Container
| Field | Type | Value Space | Purpose |
| ----------------- | ------------------------- | ---------------------------------- | -------------------------------------- |
| `signed_header_1` | `SignedBeaconBlockHeader` | Container with message + signature | First conflicting signed block header |
| `signed_header_2` | `SignedBeaconBlockHeader` | Container with message + signature | Second conflicting signed block header |
### `SignedBeaconBlockHeader` Fields
| Field | Type | Value Space | Purpose |
| ------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `message.slot` | `Slot` | `uint64` in [0, 2^64-1]. **Bound to** `header_2.slot`: must match. | Slot of the block header |
| `message.proposer_index` | `ValidatorIndex` | `uint64` in \[0, `len(validators)`-1\]. **Bound to** `header_2.proposer_index`: must match. **Index into** `validators[]`. | Index of the proposer |
| `message.parent_root` | `Root` | `Bytes32`. Must differ from `header_2` in at least one root field. | Parent block root |
| `message.state_root` | `Root` | `Bytes32`. Must differ from `header_2` in at least one root field. | State root |
| `message.body_root` | `Root` | `Bytes32`. Must differ from `header_2` in at least one root field. | Block body root |
| `signature` | `BLSSignature` | `Bytes96`. **Bound to** `message` and `proposer.pubkey`: must be valid BLS signature. | BLS signature over the message |
### Relevant `BeaconState` Fields
| Field | Type | Value Space | Purpose |
| -------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------- |
| `state.slot` | `Slot` | `uint64` in [0, 2^64-1]. Derives `epoch = slot // SLOTS_PER_EPOCH`. | Compute current epoch for slashability check |
| `state.validators` | `List[Validator, VALIDATOR_REGISTRY_LIMIT]` | Length in [0, 2^40]. All proposer index references must be < len. | Validator registry |
| `state.balances` | `List[Gwei, VALIDATOR_REGISTRY_LIMIT]` | `uint64[]`. **Same length as** `validators[]`. | Validator balances (modified by slashing) |
| `state.slashings` | `Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]` | `uint64[8192]`. Indexed by `epoch % EPOCHS_PER_SLASHINGS_VECTOR`. | Slashings accumulator |
| `state.builder_pending_payments` | `Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH]` | `Container[64]`. [GLOAS] Indexed by slot position within 2-epoch window. | Pending builder payments for current and previous epoch |
### `Validator` Fields (Relevant)
| Field | Type | Value Space | Purpose |
| -------------------- | ----------- | --------------------------------------------------------------------------------- | ----------------------------------------------- |
| `pubkey` | `BLSPubkey` | `Bytes48`. Used to verify both header signatures. | Validator public key for signature verification |
| `slashed` | `bool` | `True` or `False`. **Constraint**: must be `False` for slashability. | Whether validator has been slashed |
| `activation_epoch` | `Epoch` | `uint64`. **Bound to** `state.slot`: must be `<= current_epoch` for slashability. | Epoch when validator became active |
| `exit_epoch` | `Epoch` | `uint64`. Set during slashing if `FAR_FUTURE_EPOCH`. | Epoch when validator will exit |
| `withdrawable_epoch` | `Epoch` | `uint64`. **Bound to** `state.slot`: must be `> current_epoch` for slashability. | Epoch when validator can withdraw |
______________________________________________________________________
## Output Space (State Fields Modified)
| Field | Type | Value Space | Modification |
| ------------------------------------------------------ | ----------------------- | --------------------------------------------------------------------- | ------------------------------------------------------ |
| `state.validators[proposer_index].slashed` | `bool` | `True` | **Set** to `True` |
| `state.validators[proposer_index].exit_epoch` | `Epoch` | `max(exit_epoch, current_epoch + 1)` | **Set** to exit epoch per slashing rules |
| `state.validators[proposer_index].withdrawable_epoch` | `Epoch` | `exit_epoch + EPOCHS_PER_SLASHINGS_VECTOR` | **Set** to withdrawable epoch per slashing rules |
| `state.balances[proposer_index]` | `Gwei` | `uint64`. New value = old - penalty. | **Decreased** by slash penalty |
| `state.balances[whistleblower_index]` | `Gwei` | `uint64`. New value = old + reward. | **Increased** by whistleblower reward |
| `state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR]` | `Gwei` | `uint64`. New value = old + effective_balance. | **Increased** by slashed validator's effective balance |
| `state.builder_pending_payments[payment_index]` | `BuilderPendingPayment` | Empty container. [GLOAS] Only if proposal slot within 2-epoch window. | **Cleared** to empty `BuilderPendingPayment()` |
______________________________________________________________________
## Validation Conditions
The function asserts the following conditions (failure causes revert):
1. **Slots match**: `header_1.slot == header_2.slot`
2. **Proposer indices match**:
`header_1.proposer_index == header_2.proposer_index`
3. **Headers are different**: `header_1 != header_2`
4. **Proposer is slashable**:
`is_slashable_validator(proposer, get_current_epoch(state))`
- Validator is not already slashed
- Validator is active (activation_epoch \<= current_epoch)
- Validator has not yet reached withdrawable epoch (current_epoch \<
withdrawable_epoch)
5. **Signature 1 is valid**:
`bls.Verify(proposer.pubkey, signing_root_1, signature_1)`
6. **Signature 2 is valid**:
`bls.Verify(proposer.pubkey, signing_root_2, signature_2)`
______________________________________________________________________
## Key Constants
| Constant | Value (Mainnet) | Description |
| --------------------------------------- | -------------------------- | ---------------------------------------------------------- |
| `MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA` | `4096` | Divisor for initial slash penalty (inherited from Electra) |
| `WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA` | `4096` | Divisor for whistleblower reward (inherited from Electra) |
| `SLOTS_PER_EPOCH` | `32` | Slots per epoch (used for payment index calculation) |
| `EPOCHS_PER_SLASHINGS_VECTOR` | `8192` | Length of slashings accumulator vector |
| `DOMAIN_BEACON_PROPOSER` | `DomainType('0x00000000')` | Domain for block header signing |
| `FAR_FUTURE_EPOCH` | `2^64-1` | Sentinel for "not set" epoch |
______________________________________________________________________
## Key Cross-Field Constraints
1. **Index Alignment**:
- `len(validators) == len(balances)` always
- `proposer_index` must be `< len(validators)`
2. **Header Consistency**: Both headers must have identical `slot` and
`proposer_index` fields
3. **Header Distinction**: The headers must differ in at least one of:
`parent_root`, `state_root`, or `body_root`
4. **Proposer Validity**: The `proposer_index` must reference a valid, active,
non-slashed validator
5. **Signature Binding**: Both signatures must be valid BLS signatures from the
proposer's public key over the respective header messages with the correct
domain
6. **[GLOAS] Payment Window**: Builder pending payment deletion only occurs if
proposal slot is within current or previous epoch
______________________________________________________________________
## GLOAS-Specific: Builder Pending Payment Deletion
In the GLOAS fork, when a proposer is slashed, any associated builder pending
payment is deleted if the proposal is within a 2-epoch window:
```python
proposal_epoch = compute_epoch_at_slot(slot)
if proposal_epoch == get_current_epoch(state):
# Current epoch: index in second half of array
payment_index = SLOTS_PER_EPOCH + slot % SLOTS_PER_EPOCH
state.builder_pending_payments[payment_index] = BuilderPendingPayment()
elif proposal_epoch == get_previous_epoch(state):
# Previous epoch: index in first half of array
payment_index = slot % SLOTS_PER_EPOCH
state.builder_pending_payments[payment_index] = BuilderPendingPayment()
# If proposal_epoch < previous_epoch: no payment deletion (too late)
```
### Payment Index Mapping
| Proposal Epoch | Payment Index Formula | Array Range |
| -------------- | ------------------------------------------ | ----------- |
| Current epoch | `SLOTS_PER_EPOCH + slot % SLOTS_PER_EPOCH` | `[32, 63]` |
| Previous epoch | `slot % SLOTS_PER_EPOCH` | `[0, 31]` |
| Older | No deletion | N/A |
______________________________________________________________________
## Functions Called
Call stack for `process_proposer_slashing`. Sources: gloas functions are defined
in [gloas/beacon-chain.md](../../../../../../../specs/gloas/beacon-chain.md);
inherited functions are from earlier forks (phase0, altair, electra).
- `process_proposer_slashing(state, proposer_slashing)` — gloas
- `is_slashable_validator(validator, epoch)` — phase0
- `get_domain(state, domain_type, epoch)` — phase0
- `compute_epoch_at_slot(slot)` — phase0
- `compute_signing_root(message, domain)` — phase0
- `bls.Verify(pubkey, message, signature)` — BLS library
- `get_current_epoch(state)` — phase0
- `get_previous_epoch(state)` — phase0
- `compute_epoch_at_slot(slot)` — phase0
- `slash_validator(state, slashed_index)` — electra
- `initiate_validator_exit(state, index)` — phase0
- `decrease_balance(state, index, amount)` — phase0
- `increase_balance(state, index, amount)` — phase0
______________________________________________________________________
## Test Coverage Summary
### Inherited from Phase0 (15 tests)
| Test | Validity | Coverage |
| -------------------------------------------------- | -------- | --------------------------------------------- |
| `test_basic` | Valid | Basic proposer slashing with valid signatures |
| `test_slashed_and_proposer_index_the_same` | Valid | Block proposer slashes themselves |
| `test_block_header_from_future` | Valid | Headers from future slots are allowed |
| `test_invalid_incorrect_sig_1` | Invalid | First signature is invalid |
| `test_invalid_incorrect_sig_2` | Invalid | Second signature is invalid |
| `test_invalid_incorrect_sig_1_and_2` | Invalid | Both signatures are invalid |
| `test_invalid_incorrect_sig_1_and_2_swap` | Invalid | Signatures swapped between headers |
| `test_invalid_incorrect_proposer_index` | Invalid | Proposer index out of bounds |
| `test_invalid_different_proposer_indices` | Invalid | Headers have different proposer indices |
| `test_invalid_slots_of_different_epochs` | Invalid | Header slots are in different epochs |
| `test_invalid_headers_are_same_sigs_are_same` | Invalid | Headers are identical (same signatures) |
| `test_invalid_headers_are_same_sigs_are_different` | Invalid | Headers are identical (different signatures) |
| `test_invalid_proposer_is_not_activated` | Invalid | Proposer has not yet activated |
| `test_invalid_proposer_is_slashed` | Invalid | Proposer is already slashed |
| `test_invalid_proposer_is_withdrawn` | Invalid | Proposer is already withdrawn |
### GLOAS-Specific (3 tests)
| Test | Coverage |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `test_builder_payment_deletion_current_epoch` | Builder pending payment is deleted when proposer is slashed in the same epoch as the proposal |
| `test_builder_payment_deletion_previous_epoch` | Builder pending payment is deleted when proposer is slashed in the epoch after the proposal |
| `test_builder_payment_deletion_too_late` | Builder pending payment is NOT deleted when slashing comes more than 2 epochs after the proposal |
______________________________________________________________________
## Summary Diagram
```text
+-----------------------------------------------------------+
| INPUT (Read) |
+-----------------------------------------------------------+
| ProposerSlashing: |
| signed_header_1.message.slot : uint64 |
| signed_header_1.message.proposer_index : uint64 |
| signed_header_1.message.parent_root : Bytes32 |
| signed_header_1.message.state_root : Bytes32 |
| signed_header_1.message.body_root : Bytes32 |
| signed_header_1.signature : Bytes96 |
| signed_header_2 (same fields) |
| BeaconState: |
| slot : uint64 |
| validators[proposer_index] : Container |
| .pubkey, .slashed, .activation_epoch, |
| .exit_epoch, .withdrawable_epoch |
| builder_pending_payments[0..63] : Container[] |
+---------------------------+-------------------------------+
|
v
+-----------------------------------------------------------+
| process_proposer_slashing() |
| |
| 1. Validate header slots match |
| 2. Validate proposer indices match |
| 3. Validate headers are different |
| 4. Validate proposer is slashable |
| 5. Verify signature 1 |
| 6. Verify signature 2 |
| 7. [GLOAS] Delete builder pending payment if in window |
| 8. Execute slash_validator() |
+---------------------------+-------------------------------+
|
v
+-----------------------------------------------------------+
| OUTPUT (Modified) |
+-----------------------------------------------------------+
| validators[proposer_index].slashed : True |
| validators[proposer_index].exit_epoch : Epoch |
| validators[proposer_index].withdrawable_epoch : Epoch |
| balances[proposer_index] : decreased |
| balances[whistleblower_index] : increased |
| slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] : increased|
| builder_pending_payments[payment_index] : cleared |
+-----------------------------------------------------------+
```