diff --git a/EIPS/eip-7804.md b/EIPS/eip-7804.md new file mode 100644 index 0000000000000..9a80ecc0b4a5b --- /dev/null +++ b/EIPS/eip-7804.md @@ -0,0 +1,220 @@ +--- +eip: 7804 +title: Withdrawal Credential Update Request +description: Allow validators to update their withdrawal credentials via execution requests +author: Lucas Saldanha (@lucassaldanha), Mikhail Kalinin (@mkalinin) +discussions-to: https://ethereum-magicians.org/t/eip-7804-withdrawal-credential-update-request/21514 +status: Draft +type: Standards Track +category: Core +created: 2024-10-31 +--- + +## Abstract + +This proposal defines a mechanism to allow validators to update their withdrawal +credentials using a new execution request type (0x03). The request allows for +changing the execution address and the withdrawal credential prefix (0x01 or 0x02). + +## Motivation + +When the ability to update a validator BLS withdrawal credentials to execution +address was introduced in Capella, one of the most common questions was about +allowing the withdrawal credential to be changed in the future. +Either for security (e.g. credential rotation) or to allow for alternative ways +of handling withdrawals (e.g. having a contract address as credentials). +The main reason for not adding this options was because implementing +this communication channel between the Execution Layer and the Consensus Layer is +complex (based on the experience with the Eth1 bridge). + +In Electra, the protocol was upgraded with Execution Requests (deposits, withdrawals and +consolidations), and a mechanism for general purpose execution requests +[EIP-7685](./eip-7685.md), decreasing the complexity of adding +a new request from execution to consensus layer. + +The introduction of execution requests that are created on the execution layer, +opened up possibilities on how validators can be managed. +Execution request can be created via smart contracts, allowing for decentralized +and on-chain mechanisms to be explored. + +For an execution request to be authorized in the consensus layer, the withdrawal +credential of the validator must be the same of the `msg.caller` when processing +the transaction on the EVM. +So the validator's withdrawal credential must be the same address of the smart contract +creating the request (not the credential of the transaction sender), or the contract +needs to use `DELEGATECALL` to ensure the caller address matches the address in the +validator's withdrawal credential. +Validators that already have a contract address as their withdrawal credentials should +be able to update their contracts to meet this demand. + +There problems for existing validators that have their withdrawal credentials +set to an EOA account is they will never be able to use use smart contracts +for creating execution requests, because the current design does not allow for these +credentials to ever be changed. + +Allowing for validators to update their withdrawal credentials mean they can opt-in +and out of different schemes and strategies on managing their validators, favouring +experimentation and innovation. +Today, the only alternative to this is exitting the validator and creating a new +validator with different withdrawal credentials. + +Being able to update their withdrawal credentials also means they could move between +execution and compounding withdrawal credentials. This is not only benefitial due to +the flexibility it provides, but also solves problem that upon a validator consolidation +[EIP-7251](./eip-7251.md), the target validator withdrawal +credential is updated to 0x02, with no way to ever going back to 0x01. But because the +authentication of the message is only concerned with the source validator, a validator +can be forced to change to 0x02. If we implement the mechanism for them to change it +back 0x01, we solve this problem as well. + +## Specification + +### Constants + +| Name | Value | Comment | +| - | - | - | +|`FORK_TIMESTAMP` | *TBD* | Mainnet | + +### Configuration + +| Name | Value | Comment | +| - | - | - | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_PREDEPLOY_ADDRESS` | `0x09Fc772D0857550724b07B850a4323f39112aAaA` | Where to call and store relevant details about the withdrawal credentials update mechanism | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_TYPE` | `0x03` | The [EIP-7685](./eip-7685.md) type prefix for withdrawal credential update request | +| `SYSTEM_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe` | Address used to invoke system operation on contract +| `EXCESS_WITHDRAWAL_CREDENTIALS_UPDATE_REQUESTS_STORAGE_SLOT` | 0 | | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_COUNT_STORAGE_SLOT` | 1 | | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_QUEUE_HEAD_STORAGE_SLOT` | 2 | Pointer to head of the withdrawal credential update request message queue | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_QUEUE_TAIL_STORAGE_SLOT` | 3 | Pointer to the tail of the withdrawal credential update request message queue| +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_QUEUE_STORAGE_OFFSET` | 4 | The start memory slot of the in-state withdrawal credential update request message queue| +| `MAX_WITHDRAWAL_CREDENTIALS_UPDATE_REQUESTS_PER_BLOCK` | 4 | Maximum number of withdrawal credential update requests that can be dequeued into a block | +| `TARGET_WITHDRAWAL_CREDENTIALS_UPDATE_REQUESTS_PER_BLOCK` | 1 | | +| `MIN_WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_FEE` | 1 | | +| `WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_FEE_UPDATE_FRACTION` | 17 | | +| `EXCESS_INHIBITOR` | `2**256-1` | Excess value used to compute the fee before the first system call | + +### Execution Layer + +* **`FORK_BLOCK`** -- the first block in a blockchain with the `timestamp` greater or equal to `FORK_TIMESTAMP`. + +#### Withdrawal Credentials Update request operation + +The new withdrawal credential update request operation is an [EIP-7685](./eip-7685.md) request +with type `0x03` and consists of the following fields: + +1. `pubkey`: `Bytes48` +2. `old_address`: `Bytes20` +3. `new_address`: `Bytes20` + +The [EIP-7685](./eip-7685.md) encoding of a withdrawal credential update request is computed as follows. + +```python +request_type = WITHDRAWAL_CREDENTIALS_UPDATE_REQUEST_TYPE +request_data = read_withdrawal_credential_update_requests() +``` + +#### Withdrawal Credentials Update Request Contract + +The contract is similar to other execution requests contracts for Deposits, Withdrawals and Consolidation. +The contract has three different code paths, which can be summarized at a high level as follows: + +1. Add request - requires a `68` byte input, the validator's public key concatenated with the new address for withdrawal credentials. +2. Excess requests getter - if the input length is zero, return the current excess requests count. +3. System process - if called by system address, pop off the withdrawal credential update requests for the current block from the queue. + + + +#### Block processing + +At the end of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. after processing all transactions and after performing the block body requests validations) client software **MUST** include a call the contract as `SYSTEM_ADDRESS` and empty input data to trigger the system subroutine execute. The resopnse should be treated as a new request type (0x03) according to [EIP-7685](./eip-7685.md). + +#### Block Validation + +EL must check that the commitment hash in the execution block header matches the hash of the list of execution requests the CL sends when +validating the execution block (including any requests of the the new defined type 0x03). + +### Consensus Layer + +Full specification + +Summary of changes: + +* New container `WithdrawalCredentialUpdateRequest` +* New method in Block Processing: `process_withdrawal_credential_update_request` +* New Beacon State mutator method: `update_withdrawal_credentials` + +```python +class WithdrawalCredentialUpdateRequest(Container): + validator_pubkey: BLSPubkey + old_address: ExecutionAddress # request contract will set this to msg.caller + new_address: ExecutionAddress +``` + +```python +def process_withdrawal_credential_update_request(state: BeaconState, withdrawal_credentials_update_requests: WithdrawalCredentialUpdateRequest) -> None: + validator_pubkeys = [v.pubkey for v in state.validators] + # Verify pubkey exists + request_pubkey = withdrawal_credentials_update_requests.validator_pubkey + if request_pubkey not in validator_pubkeys: + return + index = ValidatorIndex(validator_pubkeys.index(request_pubkey)) + validator = state.validators[index] + + # Verify withdrawal credentials + has_correct_credential = has_execution_withdrawal_credential(validator) + is_correct_old_address = ( + validator.withdrawal_credentials[12:] == withdrawal_credentials_update_requests.old_address + ) + if not (has_correct_credential and is_correct_old_address): + return + + credential_type = withdrawal_credentials_update_requests.type + is_eth1_type = credential_type == ETH1_ADDRESS_WITHDRAWAL_PREFIX + is_compounding_type = credential_type == COMPOUNDING_WITHDRAWAL_PREFIX + # Verify valid type + if not (is_eth1_type or is_compounding_type): + return; + + update_withdrawal_credentials(state, index, credential_type, withdrawal_credentials_update_requests.new_address) +``` + +```python +def update_withdrawal_credentials(state: BeaconState, index: ValidatorIndex, new_credential_type: Bytes1, new_withdrawal_credentials: ExecutionAddress) -> None: + old_credential_type = validator.withdrawal_credentials[:1] + + validator = state.validators[index] + validator.withdrawal_credentials = ( + new_credential_type + + b'\x00' * 11 + + new_withdrawal_credentials + ) + + # If moving from 0x01 to 0x02 + if (old_credential_type == ETH1_ADDRESS_WITHDRAWAL_PREFIX and new_credential_type == COMPOUNDING_WITHDRAWAL_PREFIX) { + #TODO: in this case we need to ensure we put them through the churn/likely re-using some of the exiting rules + switch_to_compounding_validator(state, index) + } +``` + +## Rationale + + + +## Backwards Compatibility + + + +## Test Cases + + + +## Security Considerations + +Ownership is defined based on control of the withdrawal credential account, either as a private key (for EOA accounts) or controlling +the smart contract at the address set as withdrawal credential. Therefore allowing an update should not bring any security implications. + +Futher discussion needed. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md).