The uncontrolled growth of blockchain state is one of the greatest issues that plagues the EVM. In the long term, it can lead to degraded client performance and loss of decentralization.
I gather that the modular architecture is an implicit solution, i.e. Fuel is designed to run in multiple configurations, but I wonder if there are any explicit plans to address the growth of state rent within any particular instance of Fuel?
For instance, has there been any thought put into charging state rent?
Our primary focus is towards preventative measures. We highly encourage and support applications adopting stateless architectures via predicates. We tend to frame “state” as any data which is generally expensive to update due to merkle trees. When merkleized state is unavoidable the overhead between dapps is minimized because we intentionally avoid any form of global state roots during execution. Accessibility is further increased due to architectural decisions that enable trust-minimized light clients, which also mitigates the issue of excessive state growth for the majority of users.
For the primary configuration we’re opting to avoid the complexity of state rent as there are many potential UX hiccups and technical complications. For example, automatic rent collection on a UTXO set is less than straightforward. As a modular system, carrying out experiments like state rent could be feasible on a separate configuration but it’s not a direction we’d like to pursue at the moment.
Even with stateless dapps and light clients, state growth for full nodes is still a fundamental concern. Our current thinking is to determine appropriate target rates for state growth, and adjust pricing as needed if this rate is exceeded. While this doesn’t fully address the concern of perpetual state growth, it makes it more manageable. An appropriate rate of state growth should be calibrated based on target operating costs and TPS, and subsequently adjusted based on improvements to storage and compute hardware. Again, since we don’t have a global state root, we won’t see the same performance degradation as Ethereum does if state is allowed to grow over a long period. Ideally these constraints would be realized in the form of multi-dimensional EIP-1559 style pricing, but there’s still a lot of research to do in this area and needs to roll-out in a phased approach as a series of upgrades.
Since state tree overhead is localized to contracts by avoiding a global root, we also have the option to adjust state fees for contracts independently as they grow in size. This is a big improvement compared to having a global shared cost of state, which penalizes everyone on the chain equally.
In the worst case scenario, there are other fallback options such as regenesis events to archive unused state. While this has been an area of heated discussion on Ethereum for quite some time, regenesis events on layer 2’s and modular execution layers are much more straight-forward to carry out and already a common practice for some chains. State regenesis is far from preferable, so our primary goal is to avoid it at all costs by encouraging stateless scalable dapp architectures. This goal will be accomplished through a combination of economic techniques, education and developer tooling.
Wonderful answer, thanks for the detailed write-up. Overall I like your strategy - seems to be a patchwork of several well thought out decisions.
I have a few follow-up questions.
Is this because the UTXOs would need to split into multiple UTXOs, some which pay the blockchain the rent, and some which remain in the contracts’ balances?
Interesting! Can you share a case in point of a rollup that has gone through a regenesis event?
Interesting. Can you share an example of a dapp that on Ethereum would consume a lot of state but could be ported to a stateless design on Fuel?
It’s entirely possible to make the sequencer pick utxos and split them on behalf of users to pay for rent, but this creates a point of contention and could lead to a higher failure rate of transactions if the rent is deducted while a user is trying to spend the coin on something else at the same time. It also doesn’t work well if someone was using a wallet that randomizes their utxo addresses. Just to name a few reasons.
I’m less familiar with other true-rollups in particular that have done regenesis, but it’s a very common technique in the cosmos ecosystem and other chains like flow (iirc had to do regenesis every few months due to inefficiencies in their state tree)
A prime example would be transitioning from an AMM DEX to a predicate based on-chain orderbook like the Spark project. Using predicates curbs excessive use of state trees and unlocks more opportunities for parallelism. You can also convert smart wallet contracts into predicates to substantially reduce fees. Another interesting idea to explore would be seeing if it’s possible to do mass asset issuance that avoids contract interactions entirely, as issuance events would typically consume a lot of tree based state. One tradeoff to consider though, is that complex predicate interactions can be difficult to keep track of via plain node RPC queries. This is a big reason why we are developing our own general purpose indexer which is tightly integrated with the fuel data model and sway abis.