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

Fix SELFDESTRUCT incorrect account zeroing & missing beneficiary balance in witness #378

Merged
merged 3 commits into from
Feb 16, 2024

Conversation

jsign
Copy link
Collaborator

@jsign jsign commented Feb 13, 2024

This PR fixes SELFDESTRUCT behavior aligned with EIP-6780:

  • It fixes a bug where we were doing "zero writes" to the tree in the case the contract calling SELFDESTRUCT was created in the same tx.
  • If fixes a bug where we weren't doing proper touching in the witness for the SELFDESTRUCT beneficiary.

It also adds four tests to cover different cases.

0xFF, // SELFDESTRUCT
}
selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a")
_, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I prefered to run two blocks:

  • First block creates the contract.
  • Second block runs the contract.

Technically, we could do this with two tx in the same block -- but separating in blocks is better to avoid the indirect "merge" of witnesses of the two tx in a same block.

Comment on lines +902 to +1135
// The original balance was 42.
var fourtyTwo [32]byte
fourtyTwo[0] = 42
if *balanceStateDiff.CurrentValue != fourtyTwo {
t.Fatalf("the pre-state balance before self-destruct must be 42")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The pre-state of the second block for the balance should've 42, which is the balance sent when the contract was created in the first block.

Comment on lines +909 to +1140
// The new balance must be 0.
if *balanceStateDiff.NewValue != zero {
t.Fatalf("the post-state balance after self-destruct must be 0")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Post-state should be zero, since it was sent to the beneficiary.

Comment on lines +933 to +1170
if balanceStateDiff.CurrentValue == nil {
t.Fatalf("codeHash.CurrentValue must not be empty")
}
if balanceStateDiff.NewValue == nil {
t.Fatalf("codeHash.NewValue must not be empty")
}
preStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.CurrentValue[:])
postStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.NewValue[:])
if postStateBalance-preStateBalance != 42 {
t.Fatalf("the post-state balance after self-destruct must be 42")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The beneficiary pre and post state balance should have increased by 42.

Comment on lines +1000 to +1237
selfDestructContract := []byte{
0x73, // PUSH20
0x61, 0x77, 0x84, 0x3d, 0xb3, 0x13, 0x8a, 0xe6, 0x96, 0x79, 0xA5, 0x4b, 0x95, 0xcf, 0x34, 0x5E, 0xD7, 0x59, 0x45, 0x0d, // 0x6177843db3138ae69679A54b95cf345ED759450d
0xFF, // SELFDESTRUCT
}
selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a")
_, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 1, func(i int, gen *BlockGen) {
gen.SetPoS()
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
gen.AddTx(tx)
})
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here the strategy is to directly SELFDESTRUCT in the contract deployment.

Comment on lines +1031 to +1264
if balanceStateDiff.CurrentValue != nil {
t.Fatalf("the pre-state balance before must be nil, since the contract didn't exist")
}

if balanceStateDiff.NewValue != nil {
t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the self-destructed contract, both the pre and post state should be nil. This checks the bug that was found in Kaustinen.

t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all")
}
}
{ // Check self-destructed target in the witness.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The beneficiary checks are the same as the previous test.

core/state_processor_test.go Show resolved Hide resolved
Comment on lines +1159 to +1509
if balanceStateDiff.NewValue != nil {
t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Double check that despite the beneficiary is the contract itself, it doesn't appear in the post state. (i.e: the ETH was "burned").

@jsign jsign changed the title Fix SELFDESTRUCT behavior Fix SELFDESTRUCT incorrect account zeroing & missing beneficiary balance in witness Feb 13, 2024
Copy link
Owner

@gballet gballet left a comment

Choose a reason for hiding this comment

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

I think there's an issue in the case when the selfdestructed contract is also the beneficiary, since the balance will be read anyway. The rest LGTM.

Comment on lines -222 to -224
for i := 0; i < verkle.NodeWidth; i++ {
values[i] = zero[:]
}
Copy link
Owner

Choose a reason for hiding this comment

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

oh I see, this code should never have made it to this branch ! 🤯 This is meant for the replay to simulate an account destruction. It shouldn't be there. Well, that clearly explains this bug then.

core/vm/instructions.go Outdated Show resolved Hide resolved
@jsign jsign requested a review from gballet February 14, 2024 18:00
Comment on lines +533 to +539
func (s *StateDB) WasCreatedInCurrentTx(addr common.Address) bool {
stateObject := s.getStateObject(addr)
if stateObject == nil {
return false
}
return stateObject.created
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Quite unfortunate that we need to add an extra method to StateDB, but it's a simple one.
Open to renaming the method if sounds too verbose.

Comment on lines +1155 to +1384
// The way 6780 is implemented today, it always SubBalance from the self-destructed contract, and AddBalance
// to the beneficiary. In this case both addresses are the same, thus this might be optimizable from a gas
// perspective. But until that happens, we need to honor this "balance reading" adding it to the witness.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Explaining a bit why we need this test since might not be obvious for future readers.

Comment on lines +1184 to +1415
// Note that the SubBalance+AddBalance net effect is a 0 change, so NewValue
// must be nil.
if balanceStateDiff.NewValue != nil {
t.Fatalf("the post-state balance after self-destruct must be empty")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is an important check.

// 1. The contract was created in the same transaction: the balance is already touched (no need to touch again)
// 2. The contract wasn't created in the same transaction: there's no net change in balance,
// and SELFDESTRUCT will perform no action on the account header. (we touch since we did SubBalance+AddBalance above)
if contractAddr != beneficiaryAddr || interpreter.evm.StateDB.WasCreatedInCurrentTx(contractAddr) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The added || ... condition.

@jsign jsign marked this pull request as ready for review February 14, 2024 18:01
Signed-off-by: Ignacio Hagopian <[email protected]>
Signed-off-by: Ignacio Hagopian <[email protected]>
@jsign jsign force-pushed the jsign-self-destruct-bug branch from faeef6d to 434aa40 Compare February 15, 2024 16:41
Signed-off-by: Ignacio Hagopian <[email protected]>
Copy link
Owner

@gballet gballet left a comment

Choose a reason for hiding this comment

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

LGTM

@gballet gballet added this to the verkle-gen-devnet-5 milestone Feb 16, 2024
@gballet gballet merged commit 5f05456 into kaustinen-with-shapella Feb 16, 2024
2 of 3 checks passed
@jsign jsign mentioned this pull request Mar 5, 2024
gballet pushed a commit that referenced this pull request May 7, 2024
…nce in witness (#378)

* fix SELFDESTRUCT witness recording

Signed-off-by: Ignacio Hagopian <[email protected]>

* add selfdestruct tests

Signed-off-by: Ignacio Hagopian <[email protected]>

* solve lint nit

Signed-off-by: Ignacio Hagopian <[email protected]>

---------

Signed-off-by: Ignacio Hagopian <[email protected]>
gballet pushed a commit that referenced this pull request May 8, 2024
…nce in witness (#378)

* fix SELFDESTRUCT witness recording

Signed-off-by: Ignacio Hagopian <[email protected]>

* add selfdestruct tests

Signed-off-by: Ignacio Hagopian <[email protected]>

* solve lint nit

Signed-off-by: Ignacio Hagopian <[email protected]>

---------

Signed-off-by: Ignacio Hagopian <[email protected]>
gballet pushed a commit that referenced this pull request May 8, 2024
…nce in witness (#378)

* fix SELFDESTRUCT witness recording

Signed-off-by: Ignacio Hagopian <[email protected]>

* add selfdestruct tests

Signed-off-by: Ignacio Hagopian <[email protected]>

* solve lint nit

Signed-off-by: Ignacio Hagopian <[email protected]>

---------

Signed-off-by: Ignacio Hagopian <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants