Thanks for the comment. I agree that the current interface is quite similar to a key-value store. The reason is that we want a filesystem-like smart contract with minimal necessary interfaces that can host a decentralized website:
- chunked-based functions are needed because we want to support reading a large BLOB, which cannot be fit in a single tx;
ls (list directory contents) may not be needed in the minimal version as most websites do not offer it to users;
- sub-directory can be achieved by allowing “/”'s in the filename, e.g., “/a/b/c/d”, so we may not need an explicit interface of sub-directory.
Note that, a key-value store may not have 1. Moreover, if the applications do need 2 or 3, we can create the extension EIPs to include the features (similar to the extensions to ERC-20/ERC-721).
Thanks for the comment. Current EVM supports two types of storage.
- local contract storage via SLOAD/SSTORE; and
- contract-code-based via CREATE/CREATE2/EXTCODECOPY.
The first storage is efficient for 32-bytes operations, but if the data is large with dynamic size, using contract-code-based storage can be more efficient in both gas and IO. The following is a table of the gas for different storage (note that the gas for put only accounts for the the-first-time put):
|Local contract (get)
||62319 (1 / 14.1x)
||387383 (1/ 2x)
||3104698 (1 / 2.82x)
We have implemented both types of storage following the standards and the code can be found here GitHub - web3q/evm-large-storage, where
- For local contract storage, we use
keccak256(filename || chunkId) as the key, and the value is an optimized version of solidity
- For contract-code-based storage, we use
keccak256(filename || chunkId) as the key, and the value is a contract, whose code contains the corresponding chunk data.
Furthermore, the code is deployed to Rinkeby to store ENS and Uniswap homepages, and works very well:
- ENS Homepage: ENS App @ 0x9e081Df45E0D167636DB9C61C7ce719A58d82E3b
- Uniswap Homepage: Uniswap Interface @ 0xC100d49e8F3d621E4438E82E4f95CF505b3E2a28
A good design question! To support large BLOB, we can definitely do bytes ranges as UNIX read/write do. However, this seems to be complicated in some cases such as read-modify-write - if a write overrides multiple physical storages (e.g., storage slots or contract code), the contract needs to read existing data/override the data/rewrite the data. Using chunk ids, we can simplify the logic and let the off-chain application determine how to use them (and do read-modify-write off-chain). What do you think?