Skip to content
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: fixed cost per word-block #1418

Closed
4 tasks
fulldecent opened this issue Sep 16, 2018 · 7 comments
Closed
4 tasks

Blockchain rent: fixed cost per word-block #1418

fulldecent opened this issue Sep 16, 2018 · 7 comments
Labels

Comments

@fulldecent
Copy link
Contributor

fulldecent commented Sep 16, 2018

This topic has come up a few times I am stealing plenty of people's ideas (please help me attribute if missing). This issue discusses a WORDS * BLOCKS fixed cost approach.

Status and discussion points

  • Circulate with @AlexeyAkhunov, @lrettig, identify other code owners
  • Review sub proposal to spend value after rent, should we merge this into the mainline proposal?
  • Vulnerability and replay attacks with "Permanent removal"
    • Nonces require resources to store
    • Removal of nonces is a problem
    • A change in [chain id, TX signing, forking process] would solve this problem, if there is appetite, I can make a separate proposal for that and it would be a prerequisite here, also affects Casper FFG and similar proposals
  • What happens to rent on SELFDESTRUCT? Probably the same thing as what happens to value.

FULL TEXT FOLLOWS


Bibliography

Preamble

 EIP: <to be assigned>
 Title: Blockchain Storage Rent Payment
 Author: William Entriken <@fulldecent>
 Type: Standard Track
 Category: Core
 Status: Draft
 Created: 2018-09-16

Motivation

Ethereum is a public utility and we are underpricing the long-term costs of storage. Storage cost can be approximately modeled as bytes * time.

Specification

New state variables (per account)

  • rent -- σ[a]_r -- an amount of value, in Wei
  • rentLastPaid -- σ[a]_p -- a block number that is set when:
    • Value is transferred into an account
    • Code is set for an account (CREATE)
    • An account's storage is updated (SSTORE)
  • storageWords -- σ[a]_w -- number of words in storage
  • rentEvictBlock -- σ[a]_e -- the block number when this account will be destructed
    • Note: it is possible that a client could implement the Yellow Paper without storing this value explicitly. It can be calculated simply on demand.

New constants

  • RENT_WORD_COST -- The rent cost, in Wei, paid for each word-block
  • RENT_ACCOUNT_COST -- The rent cost, in Wei, paid for each account-block
  • RENT_STIPEND -- The amount of rent, in Wei, given to accounts when touched

New opcodes

  • RENTBALANCE(address) -- G_BALANCE -- Similar to BALANCE
  • SENDRENT(address, amount) -- G_BASE -- Convert value to rent and send to account
    1. σ[account]_rent += amount
    2. σ[msg.sender]_balance -= amount

Updated opcodes

A new subroutine, paying for rent, is established as such:

PAYRENT(account)
    ASSERT(σ[account]_rentEviction >= NUMBER) // TODO: I'm not sure if should be > or >=
    blocks_to_pay = NUMBER - σ[account]_rentLastPaid
    cost_per_block = RENT_ACCOUNT_COST + RENT_WORD_COST * ⌈∥σ[account]_code∥ / 32⌉ + RENT_WORD_COST * σ[a]_storageWords
    rent_to_pay = blocks_to_pay * cost_per_block
    σ[account]_rent -= rent_to_pay
    σ[account]_rentLastPaid = NUMBER
    σ[account]_rentEvictBlock = NUMBER + ⌊σ[account]_rent / cost_per_block⌋
END PAYRENT
  • SSTORE(account, key, value)
    • Perform PAYRENT(account)
    • Set σ[account]_rent = MAX(σ[account]_rent, RENT_STIPEND)
    • Do normal SSTORE operation
    • If the old value was zero for this [account, key] and the new value is non-zero, then σ[account]_storageSize++
    • If the old value was non-zero for this [account, key] and the new value is zero, then σ[account]_storageSize--
  • CALL (and derivatives)
    • If value > 0 then perform PAYRENT(account)
    • Do normal CALL operation
  • CREATE
    • Set σ[account]_rent = MAX(σ[account]_rent, RENT_STIPEND)
    • Set σ[account]_rentLastPaid = HEIGHT
    • Do normal CREATE operation
    • Note: it is possible there is a pre-existing rent balance here

Updated substate

The substate tuple is defined as:

A ≡ (As, Al, At, Ar)

This includes A_t, "the set of touched accounts, of which the empty ones are deleted at the end of a transaction".

This definition is updated to: "the set of touched accounts, of which the empty ones or evicted ones (BLOCK >= σ[a]_rentEvictBlock) are deleted at the end of a transaction"

// TODO: I'm not sure if that should be > or >=

New built-in contract

  • PAYRENT(address, amount) -- Calls PAYRENT opcode

This is a convenience for humans to send Ether from their accounts and turn it into rent. Note that simple accounts (CODESIZE == 0) cannot call arbitrary opcodes, they can only call CREATE or CALL.

The gas cost of PAYRENT will be 10,000.

No changes to current opcode gas costs.

Rationale

No call

A contract will not know or react to the receipt of rent. This is okay. Workaround: if a contract really needed to know who provided rent payments then it could create a function in its ABI to attribute these payments. It is already possible to send payments to a contract without attribution by using SELFDESTRUCT.

Eviction responsibility / lazy evaluation

The specification gives responsibility for eviction to the consensus clients. This is the most predictable behavior because it happens exactly when it should. Also there need not be any incentive mechanism (refund gas, bounty) for outside participants (off chain) to monitor accounts and request removal.

This adds a computational responsibility to the clients to track eviction dates. This is possible in efficient time (at double the memory) using a double-ended priority queue (one for addressing by account address, the other for addressing by eviction date). There may be other ways of implementing this with different time-memory guarantees.

No converting rent to value

Ether converted to rent cannot be converted back. Anybody that works in accounting and knows about gifts cards should tell you this is a good idea. It makes reasoning about the system much easier.

Accounts pay rent

Yes, they pay rent. It costs money to keep their balances so we charge them rent.

You can lose all your money

Yes, if you do not pay rent for your account or contract then you lose it all. User education is required.

Alternative: spend value (Ether balance) when rent is depleted

  • Rename rentEvictBlock to rentUsingValueBlock
  • Update eviction calculation to include RENT + VALUE. Also update CALL (and friends) operations to recalculate eviction date when value is transferred. This is the new rentEvictBlock.
  • Update CALL (and friends), RENTBALANCE and SENDRENT operations. If HEIGHT >= rentUsingValueBlock then proceed as if rent started paying using value.

This alternative is a good idea, if there is support I can include this part formally in the specification. The specification is a little complicated so I like the informal definition above until we have some consent around it.

Permanent removal

All state about an account is destructed during eviction. The data cannot be recovered. That's the point.

Hint to implementers: make sure this works:

  1. Send value to a new account (gets stipend)
  2. Pay rent to that account
  3. Wait until after the rent expires (account is gone)
  4. Send value to that account (gets stipend again)
  5. Deploy a contract (CREATE) to that account (stipend gets topped off)

Economics & constants

An SSTORE executed in 2015 cost 20,000 gas and has survived about 6 million blocks. The gas price has been around 1 ~ 50 Gwei. So basically 4,000 Wei per block per word so far. Maybe storing an account is 10 times more intensive than storing a word. But actually G_transaction is 21,000 and G_sstore is 20,000 so these are similar and they can both create new accounts / words.

How about:

  • RENT_WORD_COST -- 4,000 Wei
  • RENT_ACCOUNT_COST -- 4,000 Wei
  • RENT_STIPEND -- 4,000 Wei * 360 days worth of blocks

The rent is priced in cold, hard Ether. It is not negotiated by clients, it is not dynamic. It is linear. Why is this a good idea? Because right now Ethereum is a system with multiple free variables -- Ether/gas price, gas/opcodes costs, Ether/block reward. [Add some note here about reducing a system of equations...] So the end result is that we can peg one of the values and it will be okay.

By setting the RENT price in Ether and by having the existing gas prices set based on the floating rate, there is an implicit price of ~4 gwei set into the Yellow Paper. In other words, if in the future the price of gas goes to 1 Ether then people will be upset because they will say "I paid 20,000 gas for an SSTORE" but I only got 360 days of stipend. If I paid for the rent directly I would have gotten enough rent to last until the Sun explodes." I acknowledge this complaint and do not think it is sufficient to warrant dismissing this proposal.

Q: There is a finite-ish amount of Ether and this proposal introduces a word-price in Ether, do math for me. A: The current size of Ethereum is about ~1 TB, maybe half of that is branch nodes. So that's like 15B words. There is about 100M Ether mined. The answer is that all the Ether can be spent on 400,000 terabyte-years of storage. I'm not sure if it is helpful to look at it that way.

Backwards compatibility

There is a 360-day transition period (related to the RENT_STIPEND). This requires a hard fork. On the block of the fork, every account is immediately funded with enough rent to pay for ~ 360 days' worth of their current storage requirements. The formal implementation is that this new rule is applied if any existing account has σ[account]_rentLastPaid = 0. Therefore this can be implemented by clients lazily or eagerly.

Preexisting accounts which increase their storage needs will evict sooner than 360 days.

Users will need to be educated.

@OFRBG
Copy link

OFRBG commented Sep 18, 2018

This would enable a new set of time-based implementations. For example, it would solve the missing piece for #1339, which is paying to have your function call on hold.

@5chdn
Copy link
Contributor

5chdn commented Feb 12, 2019

Can you create a PR with EIP number 1418?

@fulldecent
Copy link
Contributor Author

@5chdn Thank you, this is #1752

@fulldecent
Copy link
Contributor Author

Copying relevant discussions from EM to here.

Thank you @Arachnid and @jvluso for these points.

It is not spelled out in the proposal, but as-is this is by default a governed system. The storage cost can be changed at any time, this would require a hard fork. We are currently using the same approach with Constantinople. Specifically, EIP-1234 governs how the fixed fee for PoW miners will change.

You are both considering a market based approach ("storage constraints", "floating"). I am trying to understand more about what you mean so I can make an improved proposal. Would a proof-of-storage system be moving in the right direction of what you are thinking?

Putting a couple ideas here to illustrate, but I haven't read existing work on PoStorage yet.

  • Assume the Ethereum mainnet storage tree is 1 TB.
  • Update Ethash:
    • Currently, there is a 16 MB seed which generates a 1 GB+ dataset ("seeded dataset"). You pick a nonce and hash a bunch of times across this dataset. Your proof can be tied back to the 16 MB seed.
    • Proposed is to use the state tree as a second 1 TB dataset ("state dataset").
    • When performing Ethash, the miner chooses whether to use seeded dataset or state dataset.
  • Update the difficulty calculation:
    • Currently, blocks based on seeded dataset are valid if the hash is less than DIFFICULTY, this stays the same.
    • Propose that blocks based on state dataset are also valid if the hash is less than FULLDIFFICULTY.
    • Currently DIFFICULTY changes to target 15 seconds per block.
    • Proposed is that DIFFICULTY and FULLDIFFICULTY changes to target two constraints: 15 seconds per block and 50% of blocks use full dataset.
  • Now we can price rent based on how expensive storage is.
    • If FULLDIFFICULTY = DIFFICULTY then apparently it is very easy to run a consensus node, so rent is free.
    • If FULLDIFFICULTY is a super-low threshold because nobody is running a full node then rent increases to cover the full block reward.
    • Somehow it scales in between these two extremes.

@fulldecent
Copy link
Contributor Author

Simplify: The new convenience built-in contract can be removed. Normal humans can just use a spell to send rent to an account.

@github-actions
Copy link

There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Apr 30, 2022
@github-actions
Copy link

This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants