Attributing rent cost

rent

#1

Following DRAFT: Position paper on resource pricing here are a few though on rent.

The problem I see with rent is “who should pay for it?”.
Letting anyone pay the rent for a whole contract have the issue that the cost is not attributed to the resource consumer.
For example for a token contract, someone could split its balance into a lot of accounts, taking a large amount of storage, thus increasing the rent which must be paid for the contract.

A potential solution to it would be to have storage affected to an account which should pay for it. In the case of a token contract, the data would be stored in state[token_holder][token_contract]. Only token_contract could write there, but token_holder would have to pay the gas for it. If he doesn’t his balance would become inaccessible and he would have to pay the wake-up cost.
Separating this storage by contract, allows a token holder to choose which rents he want to pay (otherwise we would have the opposite problem, contracts writing to its storage in order to increase the account rent). In term of user experience, this could be done smoothly by putting a rent deposit when the user use the contract.
If the user fails to pay the rent only this user is affected.

In token contracts, the problem looks quite simple, but for more complex contracts, allowing a user to render the storage related to him inacessible by not paying could be problematic (concrete example: in the Kleros contract if a the staking slot of a user is not accessible, the contract would not know which account is drawn and would not be able to penalize the corresponding user for failing to vote).

A solution would be to require users to prefund an “hibernation” fee which would be used to pay the execution cost of an action when storage goes into hibernation (in the case of the Kleros contract, it would be destroy all locked tokens of this account and unstake all remaining tokens).

That would introduce new challenges in smart contract development (having to specify a hibernation procedure and not always being able to rely on storage being available).


#2

I agree this is a challenge, and I’d say it’s the single major challenge remaining in designing rent schemes! Here’s an overview of ideas we have now:

  • Require each contract to have a fixed small storage size (eg. <= 4096 bytes). If you need infinite-sized mappings for your application, you can create a new contract for each entry, where the contract’s address would be based on what mapping and what key you are accessing, and the contract’s code would be a simple code that allows the parent contract to read and write the value inside. Mapping read/writes would then turn into contract calls. This allows mapping entries to be separable from each other and individually pay rent, hibernate and wake. Note that ENS is essentially architected this way already.
  • Put storage into a Merkle tree, and only store the root inside the contract. Users would have to process historical data to figure out what Merkle branches to use client-side, and the network would only need to bother with O(1) storage. So essentially the stateless client model, but at the contract level.

A major challenge with the first approach is how to handle the following sequence of events (I’ll use an ERC20 for example):

  1. Alice sends Zachary 20 tokens. Zachary never had any tokens before, so a contract gets created to record the balance.
  2. Zachary contract expires and hibernates.
  3. Bob sends Zachary 10 tokens. Zachary does not seem to have an active contract, so a new one gets created to register the 10-token balance.

How do the 10 tokens and 20 tokens get aggregated together? And what if this is a more complex application, where aggregating together states is not a matter of simple addition?


#3

I didn’t put much thoughts into this topic so far, but I was triggered to comment because I fail to see the problem here.

Let me try to elaborate in Layman’s terms:

Behind (I guess) every smart contract there are two entities:

  1. The creator (individual or organization)
  2. Users

Why wouldn’t contracts simply have “tip jars” where anyone can put ETH to pay the rent? Every contract, i.e. entities behind it should determine who should pay the rent (the decision process can happen on-chain or off-chain).

The reasoning is that there are so many different use cases for contracts, so there can not be a one-size-fits-all solution. In some cases the creator is financially or otherwise benefiting from the contract existence (so she/them would probably be the one paying), in other cases the creator was an altruist and is not benefiting from the contract (so users should be the ones paying), and finally there certainly are many hybrid cases.

I’m probably missing something obvious, does anyone mind explaining what? :slight_smile:


#4

From a protocol perspective what makes the most sense is for each contract to be responsible for its own rent. This would run into issues like the one that you mentioned about users mooching storage, but it would also allow smart contract developers to experiment with different ways of solving that problem.

Right now, most implementations of ERC20 contracts don’t create a new contract - they would just add a line to their storage, and whoever is footing the bill to maintain the contract would have to pay slightly more for each account with a balance.

This won’t scale well once rent gets added, so a new implementation will need to be created that can hibernate individual user’s gas totals. I think it will use the CREATE2 opcode. With hibernation, it will need to prove that nothing has been created at the specified address since the creating contract was created. Alice will be able to provide that proof and create the new contract. Bob won’t, and will instead need to wake up the hibernating contract using the logic in the ERC20 implementation.


#5

I fully agree that each contract being responsible for its own rent is optimal. And I fully agree that making a new implementation of ERC20 that creates contracts as balance records is optimal.

The challenge is making a generic programming model that actually allows developers to write contracts with (close to) the same ease that they have today without needing to worry too much about thinking about who pays what rent and what the edge cases are involving different parties not paying.


#6

Creating smart contracts for each attributable user storage seems the most elegant method.
For some applications we would need to know when some contracts hibernate (in my Kleros usecase to remove them from the staking data structure, as if we don’t, we’ll draw contracts hibernating and we won’t be able to penalize them for failure to vote).

The solution to this problem would be also have a hibernate special function (something similar to the constructor special function) which is called when one contract goes to hibernation (here it would call the main Kleros contract to unstake). When the contract wakes up, a wake function would be called.

In order not to block hibernation, the hibernation should work even if the hibernate function fails. We would probably also need to set up a gas limit in advance in order not to make hibernation calls too expensive. The amount of gas could be specified at contract creation and the minimum deposit could be a function of this gas (if hibernate gas limit is high, your contract will enter in hibernation when reaching a higher deposit threshold).