State trie clearing (invariant-preserving alternative) #161

Closed
gavofyork opened this Issue Oct 24, 2016 · 13 comments

Comments

Projects
None yet
9 participants
@gavofyork

gavofyork commented Oct 24, 2016

EDITOR UPDATE (2017-08-15): This EIP is now located at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md. Please go there for the correct specification. The text below may be incorrect or outdated, and is not maintained.

Specification

a. Account creation transactions and the CREATE operation SHALL, prior to the execution of the initialisation code, increment the nonce over and above its normal starting value by one (for normal networks, this will be simply 1, however test-nets with non-zero default starting nonces will be different).

b. Whereas CALL and SUICIDE would charge 25,000 gas when the destination is non-existent, now the charge SHALL only be levied if the operation transfers more than zero value and the destination account is dead.

c. No account may change state from non-existent to existent-but-empty. If an operation would do this, the account SHALL instead remain non-existent.

d. At the end of the transaction, any account touched by the execution of that transaction which is now empty SHALL instead become non-existent (i.e. deleted).

Where:

An account is considered to be touched when it is involved in any potentially state-changing operation. This includes, but is not limited to, being the recipient of a transfer of zero value.

An account is considered empty when it has no code and zero nonce and zero balance.

An account is considered dead when either it is non-existent or it is empty.

At the end of the transaction is immediately following the execution of the suicide list, prior to the determination of the state trie root for receipt population.

An account changes state when:

  • it is the target or refund of a SUICIDE operation for zero or more value;
  • it is the source or destination of a CALL operation or message-call transaction transferring zero or more value;
  • it is the source or newly-creation of a CREATE operation or contract-creation transaction endowing zero or more value;
  • as the block author ("miner") it is recipient of block-rewards or transaction-fees of zero or more.

Notes

In the present Ethereum protocol, it should be noted that very few state changes can ultimately result in accounts that are empty following the execution of the transaction. In fact there are only four contexts that current implementations need track:

  • an empty account has zero value transferred to it through CALL;
  • an empty account has zero value transferred to it through SUICIDE;
  • an empty account has zero value transferred to it through a message-call transaction;
  • an empty account has zero value transferred to it through a zero-gas-price fees transfer.

Rationale

Same as #158 except that several edge cases are avoided since we do not break invariants:

  • that an account can go from having code and storage to not having code or storage mid-way through the execution of a transaction; [corrected]
  • that a newly created account cannot be deleted prior to being deployed.

CREATE avoids zero in the nonce to avoid any suggestion of the oddity of CREATEd accounts being reaped half-way through their creation.

@holiman

This comment has been minimized.

Show comment
Hide comment
@holiman

holiman Oct 24, 2016

Contributor

An account is considered empty when it has no code and zero nonce and balance.

For clarity, shouldn't this be

An account is considered empty when it has no code, no storage and zero nonce and balance.

Contributor

holiman commented Oct 24, 2016

An account is considered empty when it has no code and zero nonce and balance.

For clarity, shouldn't this be

An account is considered empty when it has no code, no storage and zero nonce and balance.

@chfast

This comment has been minimized.

Show comment
Hide comment
@chfast

chfast Oct 24, 2016

Contributor

@holiman I think that issue has been described in #158 and we want to ignore storage.

Contributor

chfast commented Oct 24, 2016

@holiman I think that issue has been described in #158 and we want to ignore storage.

@vbuterin

This comment has been minimized.

Show comment
Hide comment
@vbuterin

vbuterin Oct 24, 2016

Collaborator

There is one other difference between this and #158: in #158, an account being empty also requires the storage root to be null, and it's only for the purposes of the account existence extern (used to sometimes charge 25000) gas that storage is ignored. Here, it seems like we delete an account if it has no nonce, balance or code even if it has storage.

I am confused: in #158, what are the cases in which a newly created account can be deleted prior to being deployed, or an account can go from having code and storage to not having code or storage mid-way through the execution of a transaction? Would be good to get more explanation there.

Collaborator

vbuterin commented Oct 24, 2016

There is one other difference between this and #158: in #158, an account being empty also requires the storage root to be null, and it's only for the purposes of the account existence extern (used to sometimes charge 25000) gas that storage is ignored. Here, it seems like we delete an account if it has no nonce, balance or code even if it has storage.

I am confused: in #158, what are the cases in which a newly created account can be deleted prior to being deployed, or an account can go from having code and storage to not having code or storage mid-way through the execution of a transaction? Would be good to get more explanation there.

@gavofyork

This comment has been minimized.

Show comment
Hide comment
@gavofyork

gavofyork Oct 24, 2016

It's true; that said I'd argue it's a similar edge case: An account can only have storage and no code if it was put there in init and since code execution is the only means of an account's storage interacting with anything within Ethereum's state, it is therefore inert. In any case, I'm happy to require no storage, too if desired.

CREATE -- [A's init code executing under A's account] -> B.CALL -> SUICIDE(A) [A now gets deleted] -> A... {A has been "deleted" mid-way through creation yet is still the subject of execution leading to oddities should an SSTORE happen}

On the last point I think you're right - an account can't go from having code + storage to having no code + no storage within a transaction.

It's true; that said I'd argue it's a similar edge case: An account can only have storage and no code if it was put there in init and since code execution is the only means of an account's storage interacting with anything within Ethereum's state, it is therefore inert. In any case, I'm happy to require no storage, too if desired.

CREATE -- [A's init code executing under A's account] -> B.CALL -> SUICIDE(A) [A now gets deleted] -> A... {A has been "deleted" mid-way through creation yet is still the subject of execution leading to oddities should an SSTORE happen}

On the last point I think you're right - an account can't go from having code + storage to having no code + no storage within a transaction.

@vbuterin

This comment has been minimized.

Show comment
Hide comment
@vbuterin

vbuterin Oct 24, 2016

Collaborator

A has been "deleted" mid-way through creation

SUICIDE doesn't delete an account immediately; the deletion happens at the end of processing the transaction.

I'd prefer to require an empty storage root for deletion (though not for applying the 25000 gas penalty), as this way it allows the rule to be understood as nonexistence being a canonical encoding for (0, 0, '', '') rather than a weird custom rule that says that under some set of circumstances a bunch of storage keys have to be deleted too.

Collaborator

vbuterin commented Oct 24, 2016

A has been "deleted" mid-way through creation

SUICIDE doesn't delete an account immediately; the deletion happens at the end of processing the transaction.

I'd prefer to require an empty storage root for deletion (though not for applying the 25000 gas penalty), as this way it allows the rule to be understood as nonexistence being a canonical encoding for (0, 0, '', '') rather than a weird custom rule that says that under some set of circumstances a bunch of storage keys have to be deleted too.

@gavofyork

This comment has been minimized.

Show comment
Hide comment
@gavofyork

gavofyork Oct 25, 2016

SUICIDE doesn't delete an account immediately; the deletion happens at the end of processing the transaction.

Indeed. However, in this case, A is the target, B is the source. B is suiciding to A with no funds, which, in #158, has the effect of placing B on the suicides list and immediately deleting the otherwise empty A.

gavofyork commented Oct 25, 2016

SUICIDE doesn't delete an account immediately; the deletion happens at the end of processing the transaction.

Indeed. However, in this case, A is the target, B is the source. B is suiciding to A with no funds, which, in #158, has the effect of placing B on the suicides list and immediately deleting the otherwise empty A.

@gavofyork

This comment has been minimized.

Show comment
Hide comment
@gavofyork

gavofyork Oct 25, 2016

though not for applying the 25000 gas penalty
...
rather than a weird custom rule

I'm not sure I see the difference between a "weird custom rule" for one thing and exactly the same custom rule applied to something else.

Furthermore, I'm not sure of the rationale for it - if it's simply there in order to help some particular client implementation's caching optimisations then I can't say I think it's a particularly great upside compared to the wart it introduces.

gavofyork commented Oct 25, 2016

though not for applying the 25000 gas penalty
...
rather than a weird custom rule

I'm not sure I see the difference between a "weird custom rule" for one thing and exactly the same custom rule applied to something else.

Furthermore, I'm not sure of the rationale for it - if it's simply there in order to help some particular client implementation's caching optimisations then I can't say I think it's a particularly great upside compared to the wart it introduces.

@vbuterin

This comment has been minimized.

Show comment
Hide comment
@vbuterin

vbuterin Oct 25, 2016

Collaborator

if it's simply there in order to help some particular client implementation's caching optimisations

I would say it's about simplifying the interface. If you ignore storage roots for the purpose of gas calculations, it means that the only kinds of queries that can be made during EVM execution are balance, nonce, code and individual state entry queries. If you admit the possibility of state emptiness queries, then that's a fundamentally different and more complex thing. It's sort of like why we decided not to add a "find the nearest key to X in the storage tree" opcode.

Collaborator

vbuterin commented Oct 25, 2016

if it's simply there in order to help some particular client implementation's caching optimisations

I would say it's about simplifying the interface. If you ignore storage roots for the purpose of gas calculations, it means that the only kinds of queries that can be made during EVM execution are balance, nonce, code and individual state entry queries. If you admit the possibility of state emptiness queries, then that's a fundamentally different and more complex thing. It's sort of like why we decided not to add a "find the nearest key to X in the storage tree" opcode.

@gavofyork

This comment has been minimized.

Show comment
Hide comment
@gavofyork

gavofyork Oct 26, 2016

I see; but then if we're already specifying emptiness in some way that pointedly ignores storage, is it really such a weird custom rule to reuse that same definition elsewhere? Especially in light of the fact that empty-code implies that the emptiness of storage be strictly irrelevant.

I see; but then if we're already specifying emptiness in some way that pointedly ignores storage, is it really such a weird custom rule to reuse that same definition elsewhere? Especially in light of the fact that empty-code implies that the emptiness of storage be strictly irrelevant.

@obscuren

This comment has been minimized.

Show comment
Hide comment
@obscuren

obscuren Oct 26, 2016

Member

An account is considered empty when it has no code and zero nonce and zero balance.

I disagree. An account should be considered empty when the code, nonce, balance and state root is empty.

Member

obscuren commented Oct 26, 2016

An account is considered empty when it has no code and zero nonce and zero balance.

I disagree. An account should be considered empty when the code, nonce, balance and state root is empty.

@Souptacular Souptacular referenced this issue in ethereum/pm Oct 27, 2016

Closed

AllCoreDevs Meeting 8 Agenda #1

@gavofyork gavofyork referenced this issue in paritytech/parity Oct 30, 2016

Merged

EIPs 155, 160, 161 #2976

7 of 7 tasks complete

@konradkonrad konradkonrad referenced this issue in ethereum/pyethereum Oct 31, 2016

Open

Implement "spurious dragon" HF changes in `develop` #417

0 of 3 tasks complete

@janx janx referenced this issue in cryptape/ruby-ethereum Nov 7, 2016

Merged

EIP161 State Trie Clearing #5

@joeykrug

This comment has been minimized.

Show comment
Hide comment
@joeykrug

joeykrug Nov 18, 2016

Along the lines of state pruning, is there any reason we need to store multiple copies of code if the same contracts use the same code? ie if I upload y token contract and have a contract that does that / creates it a bunch of times, why does it store the code multiple times instead of just once

Along the lines of state pruning, is there any reason we need to store multiple copies of code if the same contracts use the same code? ie if I upload y token contract and have a contract that does that / creates it a bunch of times, why does it store the code multiple times instead of just once

@ethernomad

This comment has been minimized.

Show comment
Hide comment
@ethernomad

ethernomad Dec 1, 2016

This EIP needs to specify the block number (2675000).

This EIP needs to specify the block number (2675000).

@cdetrio

This comment has been minimized.

Show comment
Hide comment
@cdetrio

cdetrio Aug 15, 2017

Member

This EIP is now located at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md. Please go there for the correct specification. The text in this issue may be incorrect or outdated, and is not maintained.

Member

cdetrio commented Aug 15, 2017

This EIP is now located at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md. Please go there for the correct specification. The text in this issue may be incorrect or outdated, and is not maintained.

@cdetrio cdetrio closed this Aug 15, 2017

@axic axic referenced this issue Aug 22, 2017

Closed

State clearing #158

@pipermerriam pipermerriam referenced this issue in ethereum/py-evm Sep 26, 2017

Closed

Implement Spurious Dragon hard fork #101

@vyorkin vyorkin referenced this issue in poanetwork/mana Jun 1, 2018

Closed

Fix refund on selfdestruct #126

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