Skip to content

EIP 103 (Serenity): Blockchain rent #35

@vbuterin

Description

@vbuterin

Motivation

The Ethereum platform gives users access to a number of computational resources: particularly computational steps, memory, logs / bloom filter usage and permanent storage in the Ethereum state. For most of these resources, the gas system is roughly incentive-compatible, although price-fixing the exchange rates between these resources is, in the current framework, unfortunately required. Storage, however, is unique in that it is not ephemeral; once storage is filled, it must be stored by all full nodes forever until/if it is deliberately cleared. The cost of storage is bytes * time, but users are only paying for bytes.

A partial solution exists for Ethereum 1.0 in the form of the SSTORE clearing refund, where a user gets up to a net 10000 gas back for converting a filled storage slot into an empty slot. However, this approach is imperfect for two reasons:

  1. During a block where transaction inclusion demand does not reach the gas limit (or even where the opportunity-cost gasprice is cheaper than average), miners have the incentive to fill up storage slots so as to use them as a "bank" that expensive transactions can use to grant themselves additional gas at a time when gasprice is expensive; essentially, conducting inter-temporal gas arbitrage with the additional negative externality of requiring all full nodes to store extra data in the meantime.
  2. There is no incentive to use storage slots for a short period of time instead of a long period of time.

Proposal

A proposal for solving this is to introduce "blockchain rent": rather than charging for bytes, actually charge for bytes * time. Here is a proposed protocol for doing so:

  • In the account RLP, keep track of the variables last_accessed and total_storage_bytes. last_accessed is meant to represent the block number at which storage was most recently changed, and total_storage_bytes is meant to represent the total number of bytes in account storage.
  • When you perform an SSTORE(k, v) operation on the account, let fee = (TOTFEE[block.number] - TOTFEE[last_accessed]) * account.total_storage_bytes, where TOTFEE[n] is the sum of the data storage fees from block 0 to block n (eg. TOTFEE[n] = n * c for some constant c would work as a basic policy but one can come up with more dynamic policies that change per block). Set account.balance -= fee, account.last_accessed = block.number and account.total_storage_bytes += len(v) + (len(k) if v != '' else 0) - len(old value of account.storage[k]) - (len(k) if old value of account.storage[k] != '' else 0). If account.balance < 0, destroy the account.
  • Create a special type of ping transaction which performs a dummy SSTORE(0, account.storage[0]) operation on any desired account. Miners can run this if they see an account that needs to be deleted in order to process fees and delete it if it is in fact bankrupt. If and only if a ping leads to an account bankruptcy, it requires no gas.

For example, suppose that TOTFEE[n] = n * 10 (ie. 10 wei per block per second), a block was last accessed in block 321070, the current block is 312085, the account has a pre-existing balance of 1000, and it only has two keys in storage: {"cow": "dog", "horse": "doge"}. We should have account.last_accessed = 321070 and account.total_storage_bytes = 3 + 3 + 5 + 4 = 15. Now, suppose we have a transaction that sends 10000 wei and, in the process of executing code, sets SSTORE("moose", "deer"). We compute:

  • Balance increased from 1000 to 11000
  • fee = (TOTFEE[312085] - TOTFEE[312070]) * 15 = (3120850 - 3120700) * 15 = 2250
  • Balance decreased from 11000 to 8750 due to the fee
  • account.total_storage_bytes += len("deer") + len("moose") - 0 - 0, ie. increased by 9 for a new total of 15 + 9 = 24. The values subtracted are both zero because there was nothing pre-existing in that key.
  • account.last_accessed = 312085

Setting TOTFEE

The hardest challenge in this scheme is setting the TOTFEE function. One could set this to a static value (ie. TOTFEE[n] = c * n), but this is essentially a form of price-fixing and so is arguably hard to set in a way that is economically efficient. A somewhat more effective strategy is to set a space limit (arguably, this is no less reasonable than setting a gas limit), and targeting TOTFEE based on that value. For example, consider a policy where we have some function for setting a space limit L (eg. a flat L = 2**35 or a linearly growing L = 2**10 * (block.timestamp - genesis.timestamp)). Then, every block, we set TOTFEE[n] = TOTFEE[n-1] + 2**(20 * L / (L - total_state_size)) (ie. the fee is 2**20 wei ~= 0.001 shannon initially, then when the total state size increases to 5% of the maximum it doubles, when it increases to 10% of the maximum it doubles again, and so forth, with the doubling intervals decreasing in size and ultimately converging to a singularity at L). In theory, the amount of storage usage should increase until it hits an equilibrium equal to the marginal demand for storage at a supply somewhere less than L (in the case of the above example, roughly L / 2 is plausible); the main tradeoff to be made is in the steepness of the supply function. Steeper supply functions are more robust to mistakes made in the initial supply-function-setting process, but they also lead to more volatile rent charges.

Why should I want to support a proposal that adds in a "weird new tax" when storage usage per second is free now?

  • The SSTORE gas costs may go down, benefiting dapp developers that only use storage for short time durations
  • The disk space requirements for a full node will go down

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions