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

core/state, core/vm: implement EIP 6780 #27189

Merged
merged 4 commits into from Jul 17, 2023
Merged

Conversation

jwasinger
Copy link
Contributor

@jwasinger jwasinger commented Apr 28, 2023

EIP-6780: SELFDESTRUCT only in same transaction

SELFDESTRUCT will recover all funds to the caller but not delete the account, except when called in the same transaction as creation


I'm taking a stab at this (probably broken atm. needs to be verified with tests)

Comment on lines 476 to 488
func (s *StateDB) SendAll(addr common.Address) bool {
stateObject := s.getStateObject(addr)
if stateObject == nil {
return false
}

stateObject.sendalled = true
return true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs journalling, just like the Suicide above does it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The operations that need to be journaled are also done when this method is called: https://github.com/ethereum/go-ethereum/pull/27189/files#diff-7da47ad076053fe0f266b3088eb9ee206c7f3f8fe5a93116a3f5e25ee5074213R837-R839 .

Can move them into this method to make it clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, IIUC, you have implicitly journalled the balance movement. But you have not journalled the stateObject.sendalled=true. So if

a -> b create c (c.create = true, c.sendall = false)
...
a -> b -> c sendall to d. (c.sendall = true)
a    b revert

c now has create and sendall, and will be deleted, but it shouldn't be since it was supposedly reverted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes. good point.

Comment on lines 838 to 840
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance)
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.SendAll(scope.Contract.Address())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way this is implemented, if sendall is executed many times, it will re-sendall. Just noting it, not sure if it's explicitly stated in the spec.

  • A sends 100 wei to b
  • A -> b.Sendall( c) // sends 100 to c
  • A sends 100 wei to b
  • A -> b.Sendall(c) // sends 100 to c again

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation. This behavior seems in-line with the spec IMO.

Comment on lines 650 to 675
newobj.created = true
newobj.sendalled = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it 100% certain that the newobj is flushed, so that when the next transaction is processed, we no longer have any objects with created set to true? I am not 100% certain, off the top of my head.

(also, settings sendalled to false is not necessary, but it might be nice to do so anyway, as you have done)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accounts created in a transaction will be in journal.dirties, so stateObject.created will be set to false in StateDB.Finalise. Upon revert, a created object will be removed from StateDB.stateObjects. So it doesn't appear that there can be
a situation where a state object created in a previous transaction can have created set to true.

@adriandev111
Copy link

PLEASE DO NOT DO THIS PLEASE

Our project utilizes our own version of GasToken (similar to 1inch CHI Token) to save gas fees for our users. Although GasToken no longer saves gas, it still performs CREATE2 and SELFDESTRUCT operations in our contract logic. Our GasToken maintains an offset, consistently executing CREATE2 or SELFDESTRUCT at this offset. After this update, CREATE2 will fail if the current offset has not been properly SELFDESTRUCTed.

Our contracts are non-upgradable. After this update, most of our contract functions will revert, and user funds in our contracts, as well as liquidity in other DeFi projects, will be locked indefinitely.

We believe that making such breaking changes is highly unacceptable. We opted for non-upgradable contracts to maintain decentralization. Introducing breaking changes like this introduces significant uncertainty for developers and promotes the use of centralized, upgradable contracts in the Ethereum ecosystem.

@holiman
Copy link
Contributor

holiman commented Jun 11, 2023

This is the wrong forum for discussion -- this is a pr implementing a spec-change, so this is where we discuss the implementation, not the spec change.

You should raise this instead on e.g. forum of ethereum magicians, or somewhere listed in the discussions-to on EIP 6780. You should also be specific: what contract (what addresses), how much ether is at risk, and what is preventing you from draning these funds now, in advance. What is the usecase, and exactly how is it implemented?

@holiman holiman added the cancun label Jul 6, 2023
core/state/journal.go Outdated Show resolved Hide resolved
core/vm/instructions.go Outdated Show resolved Hide resolved
@holiman
Copy link
Contributor

holiman commented Jul 7, 2023

Random observation: EIP 6780 removes an ancient quirk. Currently, doing selfdestruct(self) makes ether vanish from the state (as opposed to being just inaccessible, it actually removes it from existence).

With 6780, that no longer happens, at least not in this implementation.

core/state/statedb.go Outdated Show resolved Hide resolved
core/state/state_object.go Outdated Show resolved Hide resolved
@jwasinger
Copy link
Contributor Author

Random observation: EIP 6780 removes an ancient quirk. Currently, doing selfdestruct(self) makes ether vanish from the state (as opposed to being just inaccessible, it actually removes it from existence).

With 6780, that no longer happens, at least not in this implementation.

So the wording of the EIP could be interpreted to mean that it is possible to burn Eth when calling selfdestruct to self within a contract that was created in the same transaction:

When SELFDESTRUCT is executed in the same transaction as the contract was created:
...
Transfer the account balance to the target and set account balance to 0

@holiman
Copy link
Contributor

holiman commented Jul 13, 2023

So the wording of the EIP could be interpreted to mean that it is possible to burn Eth when calling selfdestruct to self within a contract that was created in the same transaction:

As you remark: burn is still possible with selfdestruct-to-self in same transaction. I don't think that needs clarification, because the eip already says basically that "selfdetruct in same transaction is not modified".

However, any contracts that rely on the ability to burn via this feature will cease working. This may totally change the incentive mechanism behind the schema.

Anyway, it's not directly related to this PR.

cc @Dedaub any thoughts?

@@ -474,6 +474,18 @@ func (s *StateDB) Suicide(addr common.Address) bool {
return true
}

func (s *StateDB) Selfdestruct6780(addr common.Address) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the meaning of the returnvalue here? I guess you just mimicked the previous Suicide... but seems like it's very unused (and totally undocumented)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that appears to be the case. neither Suicide nor Selfdestruct6780 use the return value.

Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR became pretty straight-forward in the end, looks good to me. But before we merge it, I'd like some more tests covering it

func (s *StateDB) Selfdestruct6780(addr common.Address) {
stateObject := s.getStateObject(addr)
if stateObject == nil {
return
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also looks like getStateObject will never return nil here because the currently executing address cannot be a deleted state object.

@holiman
Copy link
Contributor

holiman commented Jul 15, 2023

Needs a rebase now

@holiman
Copy link
Contributor

holiman commented Jul 15, 2023

>>> build/cache/golangci-lint-1.51.1-linux-amd64/golangci-lint run --config .golangci.yml ./...
core/state/state_object.go:91: File is not `goimports`-ed (goimports)
	created   bool 
util.go:48: exit status 1
exit status 1

core/vm/eips.go Outdated Show resolved Hide resolved
core/vm/jump_table.go Outdated Show resolved Hide resolved
Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@holiman holiman added this to the 1.12.1 milestone Jul 15, 2023
@holiman holiman merged commit 988d84a into ethereum:master Jul 17, 2023
1 of 2 checks passed
@jwasinger jwasinger deleted the eip-6780 branch July 17, 2023 17:03
yperbasis added a commit to ledgerwatch/erigon that referenced this pull request Aug 6, 2023
Cherry pick ethereum/go-ethereum#27189

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
MoonShiesty pushed a commit to MoonShiesty/go-ethereum that referenced this pull request Aug 30, 2023
EIP-6780: SELFDESTRUCT only in same transaction

>     SELFDESTRUCT will recover all funds to the caller but not delete the account, except when called in the same transaction as creation

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
AskAlexSharov pushed a commit to ledgerwatch/erigon that referenced this pull request Sep 6, 2023
Cherry pick ethereum/go-ethereum#27189

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
devopsbo3 pushed a commit to HorizenOfficial/go-ethereum that referenced this pull request Nov 10, 2023
EIP-6780: SELFDESTRUCT only in same transaction

>     SELFDESTRUCT will recover all funds to the caller but not delete the account, except when called in the same transaction as creation

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
devopsbo3 added a commit to HorizenOfficial/go-ethereum that referenced this pull request Nov 10, 2023
devopsbo3 added a commit to HorizenOfficial/go-ethereum that referenced this pull request Nov 10, 2023
devopsbo3 added a commit to HorizenOfficial/go-ethereum that referenced this pull request Nov 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants