# `process_deposit_request` Test Specification
*Note*: This report is AI-generated and is not an authoritative source of truth.
## Function Signature
```python
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None
```
Location:
[specs/gloas/beacon-chain.md](../specs/gloas/beacon-chain.md)
______________________________________________________________________
## Input Space (State and Operation Fields Read)
### `DepositRequest` Container
| Field | Type | Value Space | Purpose |
| ------------------------ | -------------- | ------------------------------------------------------------------------------------------- | ---------------------------------- |
| `pubkey` | `BLSPubkey` | `Bytes48`. Identifies target builder or validator. | Lookup in builder/validator lists |
| `withdrawal_credentials` | `Bytes32` | First byte: `0x00`=BLS, `0x01`=ETH1, `0x02`=compounding, `0x03`=Builder. Bytes[12:]=address | Routing logic + address extraction |
| `amount` | `Gwei` | `uint64` in [0, 2^64-1]. Deposit amount. | Balance to add |
| `signature` | `BLSSignature` | `Bytes96`. **Constraint**: must be valid for new builders only. | Proof of possession |
| `index` | `uint64` | `uint64`. Deposit contract index (unused in Gloas routing). | Ordering (not used in this fork) |
### Relevant `BeaconState` Fields
| Field | Type | Value Space | Purpose |
| ----------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------- |
| `state.builders[*].pubkey` | `BLSPubkey` | `Bytes48`. List length in [0, 2^40]. | Check if pubkey matches existing builder |
| `state.builders[*].balance` | `Gwei` | `uint64` in [0, 2^64-1]. | Topped up for existing builder deposits |
| `state.builders[*].withdrawable_epoch` | `Epoch` | `uint64`. **Bound to** `current_epoch`: `<= current_epoch` means slot is reusable. | Find reusable builder slots |
| `state.validators[*].pubkey` | `BLSPubkey` | `Bytes48`. List length in [0, 2^40]. | Check if pubkey matches existing validator |
| `state.slot` | `Slot` | `uint64` in [0, 2^64-1]. Derives `epoch = slot // SLOTS_PER_EPOCH`. | Used for `PendingDeposit.slot` and epoch |
| `state.pending_deposits` | `List[PendingDeposit, PENDING_DEPOSITS_LIMIT]` | Length in [0, 2^27]. Validator deposits queued here. | Append validator deposits |
______________________________________________________________________
## Output Space (State Fields Modified)
| Field | Type | Value Space | Modification |
| -------------------------------- | ---------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------- |
| `state.builders` | `List[Builder, BUILDER_REGISTRY_LIMIT]` | Length may increase by 1, or existing slot reused. | **Appended/Set** - new builder added |
| `state.builders[idx].balance` | `Gwei` | `uint64`. New value = old + `deposit_request.amount`. | **Incremented** for top-up deposits |
| `state.pending_deposits` | `List[PendingDeposit, PENDING_DEPOSITS_LIMIT]` | Length increases by 1 for validator deposits. | **Appended** - `PendingDeposit` for validator |
______________________________________________________________________
## Routing Logic (New in Gloas:EIP7732)
The function routes deposits based on pubkey existence and credential prefix:
```python
is_builder = deposit_request.pubkey in builder_pubkeys
is_validator = deposit_request.pubkey in validator_pubkeys
is_builder_prefix = is_builder_withdrawal_credential(deposit_request.withdrawal_credentials)
if is_builder or (is_builder_prefix and not is_validator):
apply_deposit_for_builder(...) # immediate
return
state.pending_deposits.append(PendingDeposit(...)) # queued
```
### Routing Decision Table
| Pubkey Status | Credential Prefix | Action | Applied |
| ------------------ | --------------------- | -------------------------- | --------- |
| Existing builder | Any | Top-up builder balance | Immediate |
| Existing validator | Any (including `0x03`)| Add to `pending_deposits` | Queued |
| New pubkey | `0x03` (Builder) | Create new builder | Immediate |
| New pubkey | `0x00`/`0x01`/`0x02` | Add to `pending_deposits` | Queued |
______________________________________________________________________
## Early Exit Conditions
1. **Existing builder pubkey**: Returns after calling `apply_deposit_for_builder()` (balance topped up)
2. **New builder with `0x03` prefix**: Returns after calling `apply_deposit_for_builder()` (new builder created or rejected if invalid signature)
______________________________________________________________________
## Key Constants
| Constant | Value | Description |
| ------------------------------ | ------------------------ | ----------------------------------------- |
| `BUILDER_WITHDRAWAL_PREFIX` | `Bytes1('0x03')` | Withdrawal credential prefix for builders |
| `BLS_WITHDRAWAL_PREFIX` | `Bytes1('0x00')` | BLS withdrawal credentials |
| `ETH1_ADDRESS_WITHDRAWAL_PREFIX` | `Bytes1('0x01')` | ETH1 address withdrawal credentials |
| `DOMAIN_DEPOSIT` | `DomainType('0x03000000')` | Domain for deposit signing |
| `FAR_FUTURE_EPOCH` | `2^64-1` | Sentinel for "not set" epoch |
| `BUILDER_REGISTRY_LIMIT` | `2^40` | Max builders in registry |
| `PENDING_DEPOSITS_LIMIT` | `2^27` | Max pending deposits |
______________________________________________________________________
## Key Cross-Field Constraints
1. **Pubkey Priority**: A pubkey existing in `state.builders` takes precedence over credential prefix - deposit always goes to the builder regardless of credentials.
2. **Validator Priority over New Builder**: If pubkey exists in `state.validators`, deposit goes to validator queue even with `0x03` credentials.
3. **Signature Verification**: Only required for NEW builder deposits. Top-ups to existing builders skip signature verification.
4. **Index Reuse**: New builders may reuse indices where `builder.withdrawable_epoch <= current_epoch AND builder.balance == 0`.
5. **Slot Binding**: Validator deposits record `state.slot` in `PendingDeposit.slot` for processing order.
______________________________________________________________________
## GLOAS-Specific: Builder Deposit Handling
In the GLOAS fork, builder deposits are processed **immediately** rather than queued:
### New Builder Creation
```python
def apply_deposit_for_builder(state, pubkey, withdrawal_credentials, amount, signature):
builder_pubkeys = [b.pubkey for b in state.builders]
if pubkey not in builder_pubkeys:
# Verify signature (proof of possession)
if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature):
add_builder_to_registry(state, pubkey, withdrawal_credentials, amount)
else:
# Top-up: increase balance directly (no signature check)
builder_index = builder_pubkeys.index(pubkey)
state.builders[builder_index].balance += amount
```
### Builder Withdrawal Credentials Format
```
withdrawal_credentials[:1] == 0x03 (BUILDER_WITHDRAWAL_PREFIX)
withdrawal_credentials[1:12] == b'\x00' * 11 (padding)
withdrawal_credentials[12:] == execution_address (20 bytes)
```
### Builder Container Structure
The `Builder` container does **not** store `withdrawal_credentials` directly. Instead, the
`execution_address` is extracted from the credentials during `get_builder_from_deposit`:
```python
class Builder(Container):
pubkey: BLSPubkey
version: uint8
execution_address: ExecutionAddress # Extracted from withdrawal_credentials[12:]
balance: Gwei
deposit_epoch: Epoch
withdrawable_epoch: Epoch
```
This differs from `PendingDeposit` (for validators), which stores the full `withdrawal_credentials`.
### Index Reuse Logic
```python
def get_index_for_new_builder(state):
for index, builder in enumerate(state.builders):
if builder.withdrawable_epoch <= get_current_epoch(state) and builder.balance == 0:
return BuilderIndex(index) # Reuse exited builder's slot
return BuilderIndex(len(state.builders)) # Append new
```
______________________________________________________________________
## Difference from Electra
**Electra** simply queues all deposits:
```python
def process_deposit_request(state, deposit_request):
if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX:
state.deposit_requests_start_index = deposit_request.index
state.pending_deposits.append(PendingDeposit(...))
```
**Gloas** adds builder routing (and removes `deposit_requests_start_index` logic):
```python
def process_deposit_request(state, deposit_request):
if is_builder or (is_builder_prefix and not is_validator):
apply_deposit_for_builder(...) # immediate
return
state.pending_deposits.append(PendingDeposit(...)) # queued
```
______________________________________________________________________
## Functions Called
Call stack for `process_deposit_request`. Sources: gloas functions are defined in
[gloas/beacon-chain.md](../specs/gloas/beacon-chain.md);
inherited functions are from earlier forks.
- `process_deposit_request(state, deposit_request)` — gloas
- `is_builder_withdrawal_credential(withdrawal_credentials)` — gloas
- `apply_deposit_for_builder(state, pubkey, withdrawal_credentials, amount, signature)` — gloas
- `is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature)` — electra
- `compute_domain(DOMAIN_DEPOSIT)` — phase0
- `compute_signing_root(deposit_message, domain)` — phase0
- `bls.Verify(pubkey, signing_root, signature)` — BLS library
- `add_builder_to_registry(state, pubkey, withdrawal_credentials, amount)` — gloas
- `get_index_for_new_builder(state)` — gloas
- `get_current_epoch(state)` — phase0
- `get_builder_from_deposit(state, pubkey, withdrawal_credentials, amount)` — gloas
- `get_current_epoch(state)` — phase0
- `set_or_append_list(state.builders, index, builder)` — altair
______________________________________________________________________
## Test Coverage Summary
### New Builder Deposits (4 tests)
| Test | Coverage |
| ------------------------------------------------- | --------------------------------------------- |
| `test_process_deposit_request__new_builder` | Basic new builder with minimum deposit |
| `test_process_deposit_request__new_builder_large_amount` | New builder with 1000 ETH deposit |
| `test_process_deposit_request__new_builder_very_large_amount` | New builder with 10k ETH deposit |
| `test_process_deposit_request__new_builder_extra_gwei` | New builder with non-round amount |
### Builder Top-up Deposits (2 tests)
| Test | Coverage |
| ------------------------------------------------- | --------------------------------------------- |
| `test_process_deposit_request__builder_top_up` | Basic top-up for existing builder |
| `test_process_deposit_request__builder_top_up_large` | Large top-up (500 ETH) for existing builder |
### Invalid Signature Tests (2 tests)
| Test | Validity | Coverage |
| ------------------------------------------------- | -------- | --------------------------------------------- |
| `test_process_deposit_request__new_builder_invalid_sig` | Invalid | New builder rejected (no state change) |
| `test_process_deposit_request__builder_top_up_invalid_sig` | Valid | Top-up succeeds (sig not checked) |
### Edge Cases (1 test)
| Test | Coverage |
| ------------------------------------------------- | --------------------------------------------- |
| `test_process_deposit_request__reuses_exited_builder_slot` | New builder reuses exited builder's index |
### Routing Tests (4 tests)
| Test | Coverage |
| ------------------------------------------------- | --------------------------------------------- |
| `test_process_deposit_request__routing__builder_pubkey_validator_credentials` | Builder pubkey wins over validator credentials |
| `test_process_deposit_request__routing__validator_pubkey_builder_credentials` | Validator pubkey wins over builder credentials |
| `test_process_deposit_request__routing__validator_pubkey_validator_credentials` | Validator pubkey → validator queue |
| `test_process_deposit_request__routing__new_pubkey_validator_credentials` | New pubkey + validator credentials → queue |
______________________________________________________________________
## Summary Diagram
```text
+-----------------------------------------------------------+
| INPUT (Read) |
+-----------------------------------------------------------+
| DepositRequest: |
| pubkey : Bytes48 |
| withdrawal_credentials : Bytes32 |
| amount : uint64 |
| signature : Bytes96 |
| BeaconState: |
| builders[*].pubkey : Bytes48[] |
| builders[*].balance : uint64[] |
| builders[*].withdrawable_epoch : uint64[] |
| validators[*].pubkey : Bytes48[] |
| slot : uint64 |
+---------------------------+-------------------------------+
|
v
+-----------------------------------------------------------+
| process_deposit_request() |
| |
| 1. Check if pubkey exists in builders |
| 2. Check if pubkey exists in validators |
| 3. Check if withdrawal_credentials has 0x03 prefix |
| 4. Route: is_builder OR (is_builder_prefix AND NOT |
| is_validator) → BUILDER PATH |
| 5. Otherwise → VALIDATOR PATH |
+---------------------------+-------------------------------+
|
+-----------------------+-----------------------+
| |
v v
+-------------------------------+ +-------------------------------+
| BUILDER PATH (Immediate) | | VALIDATOR PATH (Queued) |
+-------------------------------+ +-------------------------------+
| apply_deposit_for_builder() | | pending_deposits.append( |
| New: verify sig, add to | | PendingDeposit( |
| registry | | pubkey, credentials, |
| Existing: balance += amount | | amount, signature, |
+-------------------------------+ | slot=state.slot)) |
| +-------------------------------+
v |
+-------------------------------+ v
| OUTPUT (Builder Modified) | +-------------------------------+
+-------------------------------+ | OUTPUT (Validator Modified) |
| state.builders (new or | +-------------------------------+
| balance updated) | | state.pending_deposits |
+-------------------------------+ | (PendingDeposit appended) |
+-------------------------------+
```