Discussion thread for EIP-7688: Forward compatible consensus data structures
Related
- EIP-7495: EIP-7495: SSZ StableContainer
- EIP-6493: EIP-6493: SSZ Transaction Signature Scheme
Discussion thread for EIP-7688: Forward compatible consensus data structures
Electra would be a good pick for rollout, as a new power of two is reached in number of fields in BeaconState:
Does DepositReceipt
and ExecutionLayerWithdrawalRequest
also have to be StableContainer
or are their schemas immutable across future forks?
Updated EIP-7688 to include the latest EIP-7549 PR regarding Attestation
field order:
Updated the EIP to incorporate planned Electra changes for clarity.
Also documented all the dependent types that would become immutable.
We should consider now whether some of them should become extensible as well (by converting them to StableContainer
)
RocketPool statement related to EIP-7688:
(or, with X login: x.com)
Hey, leaving some feedback on this EIP!
Iāve built a few beacon chain proof verifier implementations in Solidity at EigenLayer, so I was initially excited to hear about a stable, forward-compatible merkleization scheme. When a hard fork changes beacon state containers, this almost always means we need to perform a smart contract upgrade to remain compatible - and it would be nice to lessen that load. However, after reviewing EIP-7688, Iām less convinced that this EIP would be useful for us in the short term.
From what it sounds like, a big part of this change involves:
Validator
container, which we definitely care about)ā¦ and the point of these changes is that if we ever need to, say, add more fields to the Validator
container, we would need to define a new field outside of the Validator
container to hold those fields. So Iām picturing a new field in BeaconState
called validators_extended
or something, that maps 1:1 with the validators in the standard validators field, but contains the data in these new fields.
If thatās roughly correct, Iād like to point out that the merklization changes we need to make to accommodate forks is actually on the lighter end of fork compatibility efforts. The harder stuff to account for tends to be changes in field semantics ā or, changes to how the beacon chain fundamentally runs/processes operations.
As an example:
EIP-7251 changes how deposits are applied on the beacon chain by introducing pending balance deposits. Due to this change, processed deposits now register new validators on the beacon chain with a zero balance and create a pending balance deposit, which gets processed once per epoch to credit the deposit amount to the validatorās balance. This means there is now a period of time (>= 1 epoch) when newly-registered validators will have a zero balance.
If I understand EIP-7688 correctly, nothing in these changes would be prevented by forward-compatible merklization. This is introducing some new āpending_xā fields, and changing the semantics of how deposits are applied. However for us, this is actually a fairly annoying change to deal with - because:
EigenPod
regardless of their current balanceMerkleization changes, on the other hand, are fairly minor changes to our on-chain/off-chain proofs libraries.
So to sum this up ā Iām not convinced EIP-7688 would actually help us much, because weāre going to need to make compatibility changes to account for changing beacon chain semantics (regardless of merklization). Fundamentally, changes to the beacon chain spec will end up breaking us in some way, and merkleization specifically is extremely easy to deal with compared to the major semantic changes that tend to come with hard forks.
And if my understanding of 7688 is correct, the way you make changes to beacon chain fields in the future would be significantly more restricted, and possibly messier. Iām picturing something like āah yes we deprecated Validator.effective_balance
, you have to use Validator_Extension.new_effective_balance
, instead, because weāre storing effective balances in wei nowā
On our end supporting this hypothetical change, it doesnāt matter that the merkleization of Validator.effective_balance
is unchanged - because we now need to combine our existing Validator
proofs with a secondary proof to validate this new field in Validator_Extension
. Ultimately all this would really accomplish for us is doubling the gas cost to verify Validator
-related proofs.
I would almost rather yall just change the semantics of Validator.effective_balance
(though I shudder to think how weād accommodate something like that!)
Thanks for the thorough feedback!
Yes, your understanding is correct that with EIP-7688 certain inner types would be considered immutable (they can still be replaced with a new field at a new index), and that list capacities would be fixed at a theoretically viable value (similar to how blob lists are merkleized, with a lower runtime serialization cap), to ensure that the Merkle proof shape remains consistent across forks.
The exact list of immutable containers and capacities could still be refined. The Validator
container is a natural candidate, as it also contains various epoch numbers that are not necessary to be tracked simultaneously (benefiting from the light-weight Optional
), which is one of the prime contributors to the BeaconState
size in memory. However, it needs to be balanced against the number of total hashes necessary to compute the state root. That is the reason why the validator balance already lives in a separate list next to the less dynamic validator configuration.
Iād like to point out that the merklization changes we need to make to accommodate forks is actually on the lighter end of fork compatibility efforts.
Thanks, this is great to hear and adds a valuable perspective! One aspect to keep in mind, though, is that merkleization changes also have to be implemented by applications and EIP-4788 smart contracts that are not affected by any semantics changes. Not having EIP-7688 represents an ongoing maintenance burden and may be especially tricky when implementing cross-chain bridges across different fork schedules, or when dealing with more immutable designs such as hardware wallets. For EigenLayer, I can see how these concerns mostly donāt apply, but itās important to look at the concept of StableContainer
across the broader ecosystem.
As for your EIP-7251 example, for (1) the proof format actually changes with Prague/Electra, despite the general workings of withdrawal credentials not having fundamentally changed (besides the compounding prefix which doesnāt logically belong there to begin with). EIP-7688 would stabilize the proof format so that your smart contract does not need to support both the Dencun and Pectra proof formats (with different length) based on timestamp. For (2), that design seems to have limitations already today, because it is possible to top-up into a fully withdrawn validator and trigger an extra withdrawal that way. You should check for the validatorās state to be āExitedā as well (via the epoch field), in which case the extra initial time with a 0 balance is no longer a special case.
Regarding restrictions, thatās by design. In your example of a Gwei field becoming Wei, the field would change from uint64
to uint256
and therefore would have to be assigned to a new gindex). Existing clients could detect when the old field is no longer populated and could gracefully fail, instead of randomly trigger subtle bugs based on incorrect computation, adding extra safety. This is in line with the use of different fields to track the various epoch numbers in the Validator
structs, even though not all of them are relevant at the same time. Generally, I agree for the Validator
container, it may make sense to move it from the āimmutable typesā to the āstablecontainerā section as part of further refinements to the EIP.
As a followup Iād like to ask whether you still see EIP-7688 as generally useful, just with a lower priority, or whether you prefer the current scheme of just having to keep migrating client applications and smart contracts. A Pectra timing provides synergy as it already reindexes the BeaconState
for other reasons, but based on your post the extra maintenance effort is not significant enough to warrant rushing this into Pectra.
Do you have any examples of contracts that consume EIP-4788 roots to drive core functionality in dapps, but are not impacted by semantic changes on the beacon chain?
Iām not sure who else even uses this oracle - I figured we were probably one of the main consumers.
For (2), that design seems to have limitations already today, because it is possible to top-up into a fully withdrawn validator and trigger an extra withdrawal that way. You should check for the validatorās state to be āExitedā as well (via the epoch field), in which case the extra initial time with a 0 balance is no longer a special case.
This is fine for us, FWIW. Weāre well aware of this edge case and a fully withdrawn validator that gets topped-up after the fact will have no trouble withdrawing from our system with zero proofs required
Generally this was a very small example, Pectra at large introduces a lot of semantic changes we will need to handle - and semantic changes continue to be the bulk of our ongoing compatibility work!
As a followup Iād like to ask whether you still see EIP-7688 as generally useful, just with a lower priority, or whether you prefer the current scheme of just having to keep migrating client applications and smart contracts. A Pectra timing provides synergy as it already reindexes the
BeaconState
for other reasons, but based on your post the extra maintenance effort is not significant enough to warrant rushing this into Pectra.
Thanks for this question. IMO 7688 is generally useful! I would just personally not rush this into Pectra. Longer-term, I would be happy to reduce our ongoing beacon chain fork compatibility workload.
In the short term, I feel like (specifically) smart contract applications that consume EIP-4788 info to drive on-chain applications are super nascent. On our end, weāve explored a few different designs and only recently pivoted to a design that weāre quite happy with and utilizes EIP-4788 effectively (and will be generally compatible with Pectraās various EIPs).
However, I think as the beacon chain spec evolves and more and more 4788-consuming applications are built, I think that both the needs of these applications and the best practices for keeping up to date with beacon chain semantics will become more clear. As it is, weāve had to figure a good bit of this out by trial and error. Iām guessing anyone else building these applications is feeling the same.
My personal preference is that while this new class of applications is still being figured out that long-term initiatives like 7688 are not as highly prioritized. Later, when this class of apps is more of a solved problem, the path to addressing long-term maintenance of these apps will be more clear.
In any case, thanks so much for the thoughtful reply! I appreciate you hearing me out
Do you have any examples of contracts that consume EIP-4788 roots to drive core functionality in dapps, but are not impacted by semantic changes on the beacon chain?
StableContainer
into the EL to support wallets that wish to verify JSON-RPC response data, improving decentralization and censorship resistance as they can use any server to obtain data instead of having to rely on a trusted server with non-ideal data logging policies: https://fusaka-light.box ā StableContainer
will help provide a streamlined view to client applications that is efficient to verify.IMO 7688 is generally useful! I would just personally not rush this into Pectra. Longer-term, I would be happy to reduce our ongoing beacon chain fork compatibility workload.
Thatās great to hear. Indeed, Pectra itself is rather large already. However, there is a balance to find to avoid a perpetual low-priority situation, because consuming EIP-4788 is hindered by fork-dependent generalized indices in more advanced cross-chain use cases, built-in mobile phone system frameworks (as part of Android / iOS), or hardware wallets, which all cannot facilitate linking up their release cadence to Ethereum forks.
Lateron, it would also be appreciated to get more finegrained feedback on the classification of individual containers. For example, changing Validator
may also help reduce BeaconState
size by dropping information that is no longer needed (e.g., activation epochs for validators that have already exited). Thereās hashing overhead though, to mix in the extra bitvector for the stablecontainer (for 1M+ validator entriesā¦ at least itās somewhat cacheable across slots).