I’d like to thank everyone for their thoughtful and constructive contributions to this topic. Incentive design for byzantine-fault-tolerant systems is generally a highly non-trivial topic.
Non-technical considerations
An important argument for Kshitij’s proposal to increase storage price by 100x is its ease of implementation: it is simply an adjustment of a protocol parameter. There are other challenges, some of which bluesign summarized at the top. While initially uncomfortable or a headache, I am confident that we together can work out the problems reasonably quickly, with ample opportunities for hands-on community contributions.
In comparison, embedding the storage price into a resource is a major research and development task requiring broad changes deeply within the security-sensitive sections of the code base (I’ll provide more details below). Therefore, I think that the core-protocol R&D team would have to implement a large portion of the necessary changes.
To be transparent, I don’t think we have the engineering resources to even start this for the upcoming multiple months. We could push out other work, like stable cadence. Though, it is hard for me to see how this could be a favourable trade-off tbh.
Alternatively, we could we could keep the current storage price until we find the engineering resources, which would leave the network with its existing vulnerability surface for storage exhaustion attacks and a lack of incentives for developers to use storage economical. Also not a good option in my opinion.
Recommendation:
- Overall, I am very much in favour of working out a mature solution to storage pricing on a conceptual level. Nevertheless, I think we need to be realistic that we won’t be able to implement this within the next year or so.
- Therefore, I would suggest we also try to answer the question what an interim solution could look like.
Technical considerations
To illustrate the complexities of implementing a system where each resource knows its own storage price, it would be best to have Bastian’s input here. I am not sufficiently familiar with Cadence internals, so the following challenges are partially speculation (and likely incomplete)
- While a
UFix64
is sufficient for memorizing the storage fee payed for a resource, this would still require every single resource to be updated. - While a
StorageWrapper(owner, uuid, FlowToken.Vault)
is conceptually easy, from Cadence’s perspective the wrapper would also just be a resource as it stands right now. In other words, Cadence would need additional logic to understand that the wrapper essentially only provides system-level information, that the wrapper cannot be removed or altered by the owner and resources can’t exist without such a wrapper.- introducing a wrapper for each resource also has the risk of massively bloating the state, especially if we implement the wrapper naively using a vault
- When a developer creates an array of resources, does each array element track its own storage or do we track the storage of the array as a whole?
- depending on the approach we take, we might undermine array inlining, potentially leading to a much less efficient storage representation and significant performance degradation
- management logic for compound resources (arrays, wrappers, etc) is doable but probably there are a bunch of edge-cases to carefully consider depending on cadence internals.
Thought experiment:
What do you think about the following variation on bluesign’s proposal (?)
- We introduce a storage token (like a specialized type of gas that is only used for storage), which corresponds to a fixed amount of bytes.
- When you create a resource, the creator can either pay for the necessary storage directly in flow tokens or pay with storage tokens.
- if somebody deletes a resource, they get the amount of storage tokens corresponding to the freedy bytes.
- Important: the Flow network only allows you to buy storage for flow tokens, but never refunds any flow tokens for storage tokens. In other words, all storage every bought remains in circulation, either in used by stored resources or as storage allowance in the form of storage tokens. Reasoning:
- Essentially we tokenize the right to store stuff on the Flow blockchain. The conversion rate of bytes to/from storage tokens is an immutable constant. We already have the software in place to measure and track storage use of resources. Therefore, it is straight forward to denominate the space taken by a resource in storage tokens without memorizing any additional information in each resource.
- Thereby, we move the variable portion of the problem, i.e. the variable price per stored byte out of the implementation.
- It is hard for me to imagine a scenario where speculation on storage price is completely impossible. Bluesign presented an idea above, which introduces its own complexities (happy to elaborate further). Nevertheless, by introducing a storage token, we separate the protocol from the speculative aspects. Instead of building a custom solution just for storage, the speculation aspects can be covered by existing DeFi stacks as storage rights are now tokenized.
- On the one hand, I don’t like people buying a bunch of storage upfront to speculate on its value. On the other hand, storage that has once been allocated and is rarely touched is not a significant problem for Flow on a technical level. If priced correctly, it might even be a funding opportunity to support the network in the earlier years, because price per megabyte tends to decrease in its cost rapidly over the years.
- In comparison, repetitive mutations of the same resource have a significant runtime cost, because the protocol generates Merkle proofs for each touched register for the verification nodes under the hood.