In case of storage, the key is a data word of 32 bytes (a kind of unsigned big number) and the value is also a data word (well, in RSK and my implementation, the value could be any byte array, is up to the consumer code the interpretation of that value).
The storage looks linear BUT only the values that are not zero are saved. In Solidity, the mappings, strings and dynamic arrays occupy more than one cell that are scattered along a 32-byte address space.
Generally the internal implementation uses byte arrays as keys and values. In order to calculate the world state root, the storage is saved using a trie structure.
There are many interesting things to discuss about tries: their number of children nodes per node (ie: I found the hexary tries better to binary ones in terms of storage usage), compression, retrieve and update (ie, only save the UPDATED values at THE END of a block execution), that can affect the performance and the resource consumption of a blockchain node.
Due to the resource involved (ie disk access and space) the load and store of storage cell values cost more gas than the cost of load and store of transient memory values. Along the time, the space occupied by all the world states in a full node could be huge. This post is an idea, a proposal, to be discussed and improved, about one simple way to collect execution fees to pay that storage.
First: it DOES NOT cover the same issues than storage rent (read Ethereum Storage Rent: What should expect? and EIP-1682: Storage Rent). It’s only a “baby step” approach that could not affect the block execution performance and with low disk space footprint.
The idea is simple: to attach to each value saved in a trie node, an associated BLOCK NUMBER. At first, this block number corresponds to the last block THAT UPDATED the value.
So, it implies to save a block number (in current implementations, around 4 bytes), when create the trie node that contains the value.
Then, when the value is retrieved again in a future block, if its block number is LESS than current block number minus N (maybe N could be 100_000 blocks), then the COST if LOAD STORE is GREATER. In this way, the fees collaborate to mantain old storage.
Second, there is a minor improvement: to consider another block number with the LAST BLOCK that READS the value. I PREFER to use THE SAME block number.
But there is a minor problem: each reads should update that number for each value retrieved, and that will increase the cost of load store into memory. If that cost is translated to more gas costs, it could be excesive.
So, my idea is TO UPDATE the block number B (in case of read the value) ONLY IF B is less that current block number minus N (again, N could be 100_000).
With these two cases, the old storage cost will be covered if:
- It is read (but only in fraction of the reads)
- It is written (with more cost if the value is old)
First, the number N should be determinad, and could be different for gas increment vs detecting old reads.
Second: the additional cost in gas to pay could be proportional to the AGE of the value.
It can be implemented as a network update (hard fork) and in an incremental way (adapting the values WITHOUT associated block number along the blockchain advance, I have an idea to do this without much resource consumptions).
It could be implemented not only for STORAGE but also other values that are part of the world state, like account states.
Also, the associated block number (to a value) could be extended to EACH trie node (not only the ones with values) and be used in other algorithms (like storage garbage collection).
Pros and Cons
There should be many, but now:
- Pros: simplicity, incremental adoption
- Cons: it does not consider old storage that it is never read or written
Maybe, I will add this number to my current implementation.
Angel “Java” Lopez