# `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) | +-------------------------------+ ```