This should be indicated in the specification. Right now it only says:
a monotonically increasing index as a uint64 value that uniquely identifies each withdrawal
It should be updated to include that this number starts at 0 for the very first withdraw, and each withdraw after that has an index that is 1 more than the previous withdraw.
It may also be valuable to note that this index is global, not per-block.
Yes, this was exactly my point, since indeed monotonically increasing does not mean that it +1s for each withdrawal, it can increase with any number (integer). And you are right, it should also specify that it should start at 0.
Just to verify: this monotonically increasing the withdrawal indices which start at 0 should not be checked by the execution layer, because it is enforced by the consensus layer?
EDIT: other question: to compute the withdrawals root, I assume we put (index, withdrawalRLP) in the trie, where withdrawalRLP is the RLPd version of the withdrawal in question?
Given that withdrawals_root and the Withdrawal structure are new additions, I’m wondering if it would be possible to use a consistent tree style across the two specs.
This would enable the EL block header to be derived from the CL ExecutionPayloadHeader, enabling light clients that are subscribed to the CL light clients gossip to follow the chain without requiring any additional network requests. Network requests would only be needed when encountering a block of interest via logs_bloom, or when requesting a state or transaction inclusion proof.
I have explored three other approaches to solve this inconsistency across EL and CL root formats for light clients.
Including hexary transactions_root and withdrawals_root into the ExecutionPayload
This would require extensions to the BeaconBlock and BeaconState, hence is more invasive than it should be, just to make life easier for light clients.
May interfere with future changes to the trie format (statelessness / verkle tries).
Have the CL implementation re-compute hexary transactions_root and withdrawals_root from the beacon block data when providing light client data.
This would require CL implementations to add software for hexary trie computation. Given that SSZ tends to be seen as more modern than hexary tries, it would be a step into the wrong direction.
May interfere with split block storage designs, because the CL would no longer have the raw transactions and withdrawals that are needed to compute the hexary trie root hashes. The CL may need to fetch that info via eth_getBlockByRoot(..., includeTransactions: false), and that call may compete with resources needed for ongoing validator duties or fork choice processing.
Only provide the other ExecutionPayloadHeader fields to the light client, omitting the hexary transactions_root and withdrawals_root.
This would require light clients to issue a second network request to obtain those hexary trie roots, which are needed to validate inclusion of transactions. It would be great if light clients could follow the chain without additional network requests to individual peers, just by following gossip on light client topics (or the event stream from REST).
May introduce complexity by having not only full blocks and block headers, but also a partial block headers concept.
Hence, the request here, whether withdrawals_root and Withdrawals could be modernized with a change to SSZ. SSZ libraries should already be available for all programming languages used by major EL implementations. Going forward, new roots should become consistent across both the EL and CL.
Note that transactions_root is the other blocker currently preventing to convert ExecutionPayloadHeader to an EL block header. The situation is different, though, because the individual transactions are also RLP encoded in the CL. So, only the hexary transactions_root would need to be converted to a SSZ root, but not the individual transactions. If that is not feasible given the long-term existence of this field, the ExecutionPayload could be extended with the hexary transactions_root as a one-time exception.
I have read a lot of different things about the staking mechanism, but I’m still a little bit confused about the lifecycle.
As I understand it it has the following steps/operations
create staking and validator depositing 32 ETH
set an eth 1 address for withdrawals
begin to retrieve rewards through the EIP-4895 mechanism (march 2023)
signal a voluntary stake exit
retrieve the 32 ETH staken
So my questions/confusions are
Is this a fair representation of the lifecycle
As I understand the eth 1 address at 2) is for both 3) and 5) and can only be a Externally Owned Address not a Contract Address (as no EVM). It this correct?
is 4) supported now or when will it be and how do you do it, is there a EIP for that?
as i understand EIP-4895 the amounts are added directly to the eth1 address 2), “out of thin air”
Is that true for both 3) and 5). If so, what happens with the 32 ETH from 2) in deposit_contract.sol
As I understand the eth 1 address at 2) is for both 3) and 5)
yes
can only be a Externally Owned Address not a Contract Address (as no EVM). It this correct?
no, the execution layer address can be any valid address, so either an EOA or a smart contract. the mechanism of EIP-4895 only increments the balance so one caveat is that if you are expecting any kind of execution from the smart contract, that simply won’t happen (so no logs, or ability to update internal smart contract state)
is 4) supported now
yes
Is that true for both 3) and 5)
yes
what happens with the 32 ETH from 2) in deposit_contract.sol
nothing. the ETH in this contract should be considered to be ‘burnt’ and/or taken out of supply. there is no way to access them any more
Since we are handling a large number of validators, we are thinking about having multiple validators using the same withdrawal address, for speed and cost reasons of the further handling of funds. However that introduces a potential accounting traceability problem.
Will it be possible to somehow enumerate through all the
execution_payload.withdrawals ... List of [index, validator_index, address, amount]
specified in EIP-4895 performed on the execution layer, to trace the specific transactions per validator ?
Hey @ralexstokes! Are validator gas tips included in the automatic reward push on top of the 32 ETH or are they withdrawn in a different way? Also, can rewards and principal be withdrawn simultaneously? Thanks!
“gas tips” would fall into execution layer rewards and those go to the feeRecipient set in each block
entirely separately, validators get rewards for various duties performed at the consensus layer and those rewards are subject to withdrawals via this EIP-4895 mechanism
you can withdraw “rewards and principal” at the same time if you have exited the validator and it becomes withdrawable
Do partial withdrawals (where the remaining principal after the withdrawal is greater than 16 ETH) require the validator to be exited? If not, is this an immediate withdrawal to the beacon chain?
I was also wondering if the withdrawal operation will require the validator key and the withdrawal key or just the withdrawal key? Thanks!
Question (maybe naive).
With the recipient address on the Withdrawal object being any ETH valid address (including smart contracts), could this affect applications on the EVM assuming that the contract should never have an ETH balance?
Maybe it is just that because of SELFDESTRUCT this should simply not be assumed, but to me looks like something pretty important and new, if working like that.