-
Notifications
You must be signed in to change notification settings - Fork 13
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
Conversation
0xFF, // SELFDESTRUCT | ||
} | ||
selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") | ||
_, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { |
There was a problem hiding this comment.
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.
// 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") | ||
} |
There was a problem hiding this comment.
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.
// The new balance must be 0. | ||
if *balanceStateDiff.NewValue != zero { | ||
t.Fatalf("the post-state balance after self-destruct must be 0") | ||
} |
There was a problem hiding this comment.
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.
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") | ||
} |
There was a problem hiding this comment.
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.
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) | ||
}) |
There was a problem hiding this comment.
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.
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") | ||
} |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
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") | ||
} |
There was a problem hiding this comment.
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").
There was a problem hiding this 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.
for i := 0; i < verkle.NodeWidth; i++ { | ||
values[i] = zero[:] | ||
} |
There was a problem hiding this comment.
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.
func (s *StateDB) WasCreatedInCurrentTx(addr common.Address) bool { | ||
stateObject := s.getStateObject(addr) | ||
if stateObject == nil { | ||
return false | ||
} | ||
return stateObject.created | ||
} |
There was a problem hiding this comment.
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.
// 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. |
There was a problem hiding this comment.
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.
// 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") | ||
} |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The added || ...
condition.
Signed-off-by: Ignacio Hagopian <[email protected]>
Signed-off-by: Ignacio Hagopian <[email protected]>
faeef6d
to
434aa40
Compare
Signed-off-by: Ignacio Hagopian <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…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]>
…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]>
…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]>
This PR fixes SELFDESTRUCT behavior aligned with EIP-6780:
It also adds four tests to cover different cases.