New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blockchain rent: user-side resurrectable edition #88

Open
vbuterin opened this Issue Apr 3, 2016 · 11 comments

Comments

Projects
None yet
9 participants
@vbuterin
Copy link
Collaborator

vbuterin commented Apr 3, 2016

A previous EIP discussed the possibility of introducing a notion of "blockchain rent": paying not just a one-time fee for adding storage and getting a one-time refund for removing it, but actually paying per unit time. This was met with the primary criticism that users liked the aspect of Ethereum that contracts sit on the chain forever, and once a contract is placed there is no risk of the contract suddenly disappearing due to no longer being able to afford rent.

This EIP describes a mechanism by which contracts that "go bankrupt" and get deleted because they can no longer afford to pay rent can be removed from storage (via protocol-incentivized third-party cleanup actors that could be miners but don't have to be so as to simplify the consensus code, as usual) but can be resurrected at a later time if need be.

Mechanism

Suppose that we have a blockchain rent scheme, as in EIP 103, where contracts can get deleted. When this happens, the "repo transaction" generates a receipt in that block, specifying that the contract was removed, as well as its state root just before removal. Anyone can "resurrect" the contract at any time by providing a transaction containing the Merkle branch to the receipt of the repo transaction, plus the contract's entire state at that time. The protocol checks that the state root is equivalent to what was in the repo receipt, that the repo receipt is valid (this can be done through a scheme where the state of one block contains state roots of previous blocks, and the process of "hopping" back in time through these roots is reasonably efficient), and that an appropriate fee is paid (eg. one week's worth of rent is a possible minimum); if all checks pass, then it re-creates the contract at that address.

A special case arises if a contract gets deleted, then resurrected, then deleted again; in this case, there are multiple repo transactions to choose from. To handle this case, a challenge-response protocol will need to be added, where the first resurrection transaction only creates a "stub" that can be challenged by a newer repo receipt within X blocks, and if the stub is unchallenged then it can be replaced with a live contract containing the actual storage.

Another special case is that of a contract being deleted, then recreated with the same init code as previously, then resurrected (whether before or after a subsequent deletion). One way of dealing with this problem is to make contract creation challengeable in the same way as resurrection, though this introduces inefficiencies and inconveniences; another is to have contract addresses be based on a global nonce that changes every Y blocks (eg. two years) and require contracts to submit Y blocks worth of rent as a deposit before increasing their storage usage, so that they cannot get deleted before the global nonce changes. Note that this does mean that "pay to not-yet-existent contract" transactions will require the recipient to register their address within Y blocks, although if desired we can add a mechanism by which a user can send initialize a contract with a previous global nonce, and only in that case requiring a challenge time to make sure that there are no repo receipts for the address.

Developer Best Practices

Contract developers will have to make do with the possibility that a contract they have some relationship to gets deleted; hence, if this scheme is implemented, we encourage developers to check that the contracts they are talking to exist, and if the contract does not exist throw an exception, so that the sender can themselves resurrect and resend the original transaction if they so choose.

@Souptacular

This comment has been minimized.

Copy link
Member

Souptacular commented Apr 4, 2016

A concern I have about this scheme is how much time it would take to ressurect a contract. Contracts are becoming more complicated and reference multiple contracts at times. Systems will need to have built in mechanisms to make external, off-chain calls to get the data to ressurect contracts and by the time this occurs the initial call that would try to use the contract would time out or provide a long delay for users. To negate a delay for users, SC devs will need to create ways to query the external contracts often to make sure their services will still work.

@vbuterin

This comment has been minimized.

Copy link
Collaborator Author

vbuterin commented Apr 4, 2016

Well, unforeseen contract deletion is only something that should happen in fairly exceptional cases involving developer negligence, so I don't see too much of a problem with requiring a 3 hour waiting period on it. I would say contract resurrection can be implemented as a library if need be: if a contract or EOA calls another contract and fails because the child is empty, then check to see if the child existed previously (this can be done via a specialized kind of archive node), and if it did then grab the merkle branch to bring it back.

@Smithgift

This comment has been minimized.

Copy link

Smithgift commented Apr 4, 2016

The advantage of this proposal is that it will clean up the blockchain of old, unused contracts, as well as spam contracts. The disadvantage is the various inelegant requirements. For example, we will have to have a new kind of transaction with the right to repo contracts, or create "kernalspace" contracts that can repo others. The side effects of the challenge-response system also feel significant. Global nonce with past receipt seems the least disruptive.

The big question: Is blockchain rent to mitigate the increase the size of global state, or to actually decrease the size of the global state?

It's still possible for data to be permanently deleted in this model. Consider the following (somewhat unlikely) chain of events.

  1. Contract A is created.
  2. Contract A is deleted.
  3. The original creator of Contract A recreates it at the same address, creating A'.
  4. Due to unspecified shenanigans (spam attack, collusion, attack on archive node), A' is never challenged successfully.
  5. A' is deleted.

We now have two receipts for the address of A, and the original A's receipt can always be challenged by the A`. I doubt this will ever be an issue, and a simple technique would be to create important contracts from addresses which are promptly disposed of. (But if the contract is important, it's unlikely to get archived in the first place, let alone not be challenged when recreated.)

@chriseth

This comment has been minimized.

Copy link
Contributor

chriseth commented Apr 4, 2016

With regards to the "developer best practice": What about raising an OOG for empty destination code by default (at least if value is zero)? I can imagine that checking CODESIZE in an inner loop is quite expensive (for example it will turn into an external function call in the JIT) and it cannot be optimized away, because the library can allways selfdestruct.

@vbuterin

This comment has been minimized.

Copy link
Collaborator Author

vbuterin commented Apr 6, 2016

Agree that cleanup of unused code is the pro, and inelegance is the primary con.

With regards to the "developer best practice": What about raising an OOG for empty destination code by default (at least if value is zero)?

I would be fine with this.

@Smithgift

This comment has been minimized.

Copy link

Smithgift commented Apr 8, 2016

If we decide to rip off the lid of the Pandora's Box of making "kernalspace" contracts, I think we can find an elegant version of this underneath.

Suppose there are some opcodes that only contracts at certain addresses can use. I think all that would be necessary in this instance is the ability to create and delete contracts at arbitrary addresses and set arbitrary state. (I guess that actually suffices for most kernalspace uses?)

A precompiled kernalspace rent contract stores the deposit of various contracts. When rent is not paid, a normal transaction to the rent contract uses a delete opcode to delete the deadbeat contract. Another normal transaction sets the resurrection process in motion. If there's no challenge, it uses a create opcode to recreate the code and then some sort of state-setting opcode to recreate the local state.

Another advantage is simply having the rent contract store nonces of archives and getting rid of the challenge-response protocol entirely. I'm not sure how best to deal with manual recreation. A violent method is for the rent contract to keep track of whether contracts should currently be dead or not, and kill them if they ought to be. Wallets would have to have some sense to check the rent contract before interacting with contracts under some circumstances.

Of course, the Pandora's Box of kernalspace contracts is potentially a very large one, and that's a discussion of its own. But if we still decide to have kernalspace contracts for some other reason, this is a lot more possible.

@willnwhite

This comment has been minimized.

Copy link

willnwhite commented Mar 14, 2017

Before any rent, I think it should be easier to voluntarily save space on the blockchain by deleting contracts you've deployed. Currently that kind of code has to be written into the contract before it's deployed. I think every contract should come with a standard "delete" functionality (that only its deployer can use). Contract deployers would be incentivised to close their unused contracts so their other contracts will have more space.

As well as saving space, a built-in delete could help with the other things this code is currently manually written into a contract for, like stopping use of a deprecated contract. I had started thinking about making an EIP for this reason previously.

@willnwhite

This comment has been minimized.

Copy link

willnwhite commented May 10, 2017

I now have contracts deployed that I'd be happy to destroy, but I'm unable to because I didn't write the appropriate function into them. Also, it'd be great if it didn't cost gas to destroy contracts.

@Arachnid

This comment has been minimized.

Copy link
Collaborator

Arachnid commented May 11, 2017

@jamesray1

This comment has been minimized.

Copy link
Contributor

jamesray1 commented Nov 10, 2017

Referenced here: https://ethresear.ch/t/the-stateless-client-concept/172.

With a stateless client concept an advantage is: "All of the thorny questions about state storage economics that lead to the need for designs like rent (eg. #35 http://github.com/ethereum/EIPs/issues/872 http://github.com/ethereum/EIPs/issues/882) and even the current complex SSTORE cost/refund scheme disappear, and blockchain economics can focus purely on pricing bandwidth and computation, a much simpler problem)".

@rkagerer

This comment has been minimized.

Copy link

rkagerer commented Mar 30, 2018

Say you have a dormant contract, maybe something lots of users transacted with years ago (e.g. deposit) and now suddenly there's a burning reason to transact again (e.g. withdraw). I can just see a situation where everyone's waiting for some other poor sucker to pull the trigger first. Is it fair that one person pays the whole revival fee, then everyone else can immediately transact for "free"?

Might be prudent to have a mechanism whereby the person who swallowed the fee recovers some of it from subsequent contract invocations for a period of time (scaling off), to discourage "attrition abuse".

@ethereum ethereum deleted a comment from timelinefunds Mar 30, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment