EIP-4895 Push-style handling of beacon chain withdrawals (Beacon chain push withdrawals as operations)

Final · Consensus & staking · EIP status Final · created 2022-03-10

An EIP that passes validator withdrawals finalized on the Beacon Chain to the execution layer as a new "system-level operation", unconditionally increasing the balance of the specified address without any gas fee.

Why it is needed

Ethereum is split into the Beacon Chain, which handles consensus, and the execution layer (EVM), which processes transactions. Even when a validator's withdrawal was finalized on the Beacon Chain, there was no path to move that ETH to an account on the execution layer. This EIP defines a method in which the Beacon Chain "pushes" withdrawals into the execution layer. As soon as a withdrawal leaves the consensus layer's queue, the execution layer must process it. Instead of the earlier approach of making withdrawals a new type of ordinary transaction, the EIP separates them into a new object called an "operation", in a domain distinct from transactions. The original text gives as its reason that this reduces interactions that arise when system-level work is mixed with user data, which simplifies testing and makes security review correspondingly easier.

What changes

Today

After EIP-4895

Who is affected

In depth

Structure of the Withdrawal object

A withdrawal is a payload-level object. Its syntax is similar to a user transaction, but it belongs to a different domain. It has four fields. (1) index: a uint64 value that starts at 0 and increases by 1 for each withdrawal, acting as a global counter across the entire withdrawal sequence. (2) validator_index: the uint64 index of the consensus layer validator the withdrawal belongs to. (3) address: the 20-byte recipient address that receives the withdrawn ether. (4) amount: a nonzero uint64 amount in Gwei (1e9 wei). Serialization follows the RLP list schema [index, validator_index, address, amount].

New field withdrawals in the execution payload

A withdrawals field, an RLP list of Withdrawal data, is added to the execution payload. It is encoded after the existing fields and is treated as part of the payload body. The structure is execution_payload_rlp = RLP([header, transactions, [], withdrawals]) and execution_payload_body_rlp = RLP([transactions, [], withdrawals]). The empty list in the middle remains as a placeholder because EIP-3675 fixed the ommers value as a constant.

The withdrawals_root commitment in the header

A 32-byte field, withdrawals_root, is added to the execution payload header to commit to the list of withdrawals. It is built the same way as the existing header's transactions root. Each withdrawal is inserted into a Merkle-Patricia trie keyed by its index within the withdrawals list, and the root is taken. In the header RLP, it sits in the last position, after base_fee_per_gas.

Payload validity verification

The execution client performs additional validation on the assumption that the payload is well formed. The trie root computed from the payload's withdrawals list must equal withdrawals_root in the header. In the original pseudocode, this is assert execution_payload_header.withdrawals_root == compute_trie_root_from_indexed_data(execution_payload.withdrawals).

State transition: unconditional balance increase after transactions

The withdrawals are processed after all user transactions have been applied. For each withdrawal, the balance of the specified address is increased by amount. Because amount is in Gwei, it must be converted to wei when applied to the balance in the execution state. This balance change is unconditional and MUST not fail. There is also no associated gas cost. It applies starting from the execution timestamp FORK_TIMESTAMP (1681338455).

Why not a new transaction type

A withdrawal is an operation initiated by the system as a whole, not by an end user, so its semantics differ from existing EVM transactions. Making it a completely new object type lets ordinary EVM execution and this processing be separated like a firewall, which simplifies testing and security review of the withdrawal feature. The original text explains that this approach takes more work than the earlier approach of adding a transaction type, but it cleanly separates system-level concerns from ordinary transactions.

Why there is no gas fee, and why only balance increases

The consensus layer enforces an upper bound on the number of withdrawals that can reach the execution layer at once. This bound is set so that the operating cost on the execution layer is negligible compared with executing the whole payload. The computational cost is limited to updating a few balances in the state, and the additional storage and network burden is about 1% of the average payload size under current parameters. Also, allowing ordinary EVM execution in a withdrawal would introduce the possibility of failure and complicate accounting on the Beacon Chain side, so the design allows only balance increases, getting most of the benefit with minimal complexity.

Security considerations and current status

The key point is validating withdrawals correctly on the consensus layer, because the consensus layer must guarantee that exactly the right amount of ETH returns to the execution layer. The original text states that ETH moving from the consensus layer to the execution layer has no analogue in the existing EVM and therefore requires a very high level of security review. The status of this EIP is Final, meaning the specification is settled.

Original EIP · ← EIPs