Thanks to @adietrichs for reviewing this post.
State expiry is the current preferred solution the state growth problem.
For the purposes of this post, we’ll think of state expiry as a mechanism that, at fixed intervals, expires the entire state trie. Storing previous periods is generally orthogonal to this post, as we’re focusing on how to resurrect state that has been forgotten – regardless if n=0
or n=1
.
Here is a summary of a few proposed mechanisms:
Nothing
Nothing is as straightforward as it sounds. Simply expire state and then require users to provide witnesses for state elements they use that are not in the active state. Note that elements that are read or written to that have not been initialized in the active state must have either an exclusion proofs showing they haven’t been initialized at any point in the past or a proof showing a certain value in time and then exclusion proofs showing it hasn’t changed since then.
Pros
- really easy
Cons
- exclusion proof sizes grow linearly with the number of periods, making it extraordinarily expensive to initialize state elements
- kicks the address-collision problem down the road
Period-Aware Addresses (PWA)
The main problem with having no metadata is that initializing new state elements is really expensive. Period-aware addresses give the protocol a mechanism to avoid address collisions upon initialization of new elements. The discriminator establishes a lower bound on what period an account could have existed. There are currently two potential approaches to having PWAs:
Address Space Extension (ASE)
tldr; allow legacy 20 byte addresses and 32 byte ASE prefixed addresses. Create a context variable in the EVM to modify the behavior of opcodes that deal with addresses depending whether they’re executing in legacy or ASE mode.
Pros
- new state can be initialized without proofs
- extendable to hold other metadata
- solves the address-collision problem
Cons
- requires significant EVM changes
- creates two different EVM contexts, legacy mode and extended mode
- translation map will grow unbounded (linear in relation to the number long addresses used in legacy context), and
can’t be expiredseems like it can be expired, with a small risk of a collision after expiration - confusing UX, now potentially 3 types of addresses a user’s funds could be stored under (short, long, compressed)
- not all solidity compiled contracts mask addresses to 160 bits, so it’s possible some contracts have dirty upper bits in addresses
(more complete analysis by Ipsilon)
Extension-free PWA
tldr; find an unused 4-byte prefix and disallow new contracts / addresses from being created with it under legacy rules. Legacy contracts execute in legacy mode, creating new addresses using legacy rules. New contracts execute in PWA mode, creating new contracts where the first 4-bytes are the pre-selected prefix, bytes 5-6 are the current period, and bytes 7-20 are the address as usual.
Pros
- new state can be initialized without proofs
- relatively straightforward EVM changes
- doesn’t require translation table, users only have one address to consider
- doesn’t break existing tooling (although they may have display the xor’d address instead of the preimage?)
Cons
- makes address-collisions very practical, no more counterfactual contracts
- not an eloquent solution, may make address extension harder in the future
Period Metadata
Trie Metadata
I don’t think this idea has been formally defined (other than in @adietrichs’ head), but roughly the idea is to add a new field to the account object in the state trie denoting its creation epoch. This solves the issue of new storage initialization being very expensive due to exclusion proofs (especially assuming contracts are written to deploy new child contracts per period), but does not improve the “do nothing” case for creating new accounts.
Pros
- new storage elements can be initialized without proofs if a contract is fresh
- pretty simple
- extendable to hold other metadata
- doesn’t require unbounded state growth for external mapping
- doesn’t break existing tooling
Cons
- kicks the address-collision problem down the road
- creating new accounts is expensive, need exclusion proof from period 0 that the account never existed
External Period Registry
Also not formally defined, but the broad strokes are we introduce a new registry trie which stores state expiry metadata. The registry would not be expirable and would store the period that the contract was created. Additional information could be stored, like total number of storage elements. Any time the number of active storage elements equals the number of total storage elements for the contract, the contract period could be promoted to the current period. A scheme like this could probably also be implemented for the EWA proposals with a state trie modification.
Pros
- new state can be initialized without proofs
- extendable to hold other metadata
- doesn’t require translation table, users only have one address to consider
- doesn’t break existing tooling
Cons
- new trie structure
- grows unbounded (linear in relation to the number of addresses in use)
- kicks the address-collision problem down the road