mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
feedback + workaround for the prefunded contrat issue
Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
This commit is contained in:
parent
486da08c8f
commit
0063f267c9
3 changed files with 160 additions and 20 deletions
|
|
@ -1062,7 +1062,8 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno
|
||||||
deletes[addrHash] = op
|
deletes[addrHash] = op
|
||||||
|
|
||||||
// Short circuit if the origin storage was empty.
|
// Short circuit if the origin storage was empty.
|
||||||
if prev.Root == types.EmptyRootHash {
|
|
||||||
|
if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Remove storage slots belonging to the account.
|
// Remove storage slots belonging to the account.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -162,10 +163,20 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
|
|
||||||
// Add two contract creations in block #2
|
// Add two contract creations in block #2
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
tx, _ = types.SignTx(types.NewContractCreation(6, big.NewInt(16), 3000000, big.NewInt(875000000), code), signer, testKey)
|
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 6,
|
||||||
|
Value: big.NewInt(16),
|
||||||
|
Gas: 3000000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: code,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
|
|
||||||
tx, _ = types.SignTx(types.NewContractCreation(7, big.NewInt(0), 3000000, big.NewInt(875000000), codeWithExtCodeCopy), signer, testKey)
|
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 7,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Gas: 3000000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: codeWithExtCodeCopy,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -524,11 +535,20 @@ func TestProcessVerkleExtCodeHashOpcode(t *testing.T) {
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// Create dummy contract.
|
// Create dummy contract.
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 100_000, big.NewInt(875000000), dummyContract), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: dummyContract,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
|
|
||||||
// Create contract with EXTCODEHASH opcode.
|
// Create contract with EXTCODEHASH opcode.
|
||||||
tx, _ = types.SignTx(types.NewContractCreation(1, big.NewInt(0), 100_000, big.NewInt(875000000), extCodeHashContract), signer, testKey)
|
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 1,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: extCodeHashContract}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
} else {
|
} else {
|
||||||
tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey)
|
tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey)
|
||||||
|
|
@ -590,7 +610,11 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) {
|
||||||
common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d").Bytes(),
|
common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d").Bytes(),
|
||||||
[]byte{byte(vm.BALANCE)})
|
[]byte{byte(vm.BALANCE)})
|
||||||
|
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 100_000, big.NewInt(875000000), txData), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: txData}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -665,7 +689,12 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) {
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// Create selfdestruct contract, sending 42 wei.
|
// Create selfdestruct contract, sending 42 wei.
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(42),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: selfDestructContract,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
} else {
|
} else {
|
||||||
// Call it.
|
// Call it.
|
||||||
|
|
@ -765,7 +794,12 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
|
||||||
|
|
||||||
_, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) {
|
_, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) {
|
||||||
gen.SetPoS()
|
gen.SetPoS()
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(42),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: selfDestructContract,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -865,7 +899,12 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T)
|
||||||
gen.SetPoS()
|
gen.SetPoS()
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// Create self-destruct contract, sending 42 wei.
|
// Create self-destruct contract, sending 42 wei.
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(42),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: selfDestructContract,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
} else {
|
} else {
|
||||||
// Call it.
|
// Call it.
|
||||||
|
|
@ -928,23 +967,22 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) {
|
||||||
deployer = crypto.PubkeyToAddress(testKey.PublicKey)
|
deployer = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
contract = crypto.CreateAddress(deployer, 0)
|
contract = crypto.CreateAddress(deployer, 0)
|
||||||
)
|
)
|
||||||
// Also add the contract-to-destroy
|
|
||||||
gspec.Alloc[contract] = types.Account{
|
|
||||||
Balance: big.NewInt(100),
|
|
||||||
}
|
|
||||||
// The goal of this test is to test SELFDESTRUCT that happens in a contract
|
// The goal of this test is to test SELFDESTRUCT that happens in a contract
|
||||||
// execution which is created in **the same** transaction sending the remaining
|
// execution which is created in **the same** transaction sending the remaining
|
||||||
// balance to itself.
|
// balance to itself.
|
||||||
t.Logf("Contract: %v", contract.String())
|
t.Logf("Contract: %v", contract.String())
|
||||||
|
|
||||||
// TODO: investigate why this test succeeds regardless of whether the
|
|
||||||
// selfdestruct-to-self is used, or selfdestruct-to-zero is used.
|
|
||||||
selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)}
|
selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)}
|
||||||
//selfDestructContract := []byte{byte(vm.NUMBER), byte(vm.SELFDESTRUCT)}
|
|
||||||
|
|
||||||
_, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) {
|
_, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) {
|
||||||
gen.SetPoS()
|
gen.SetPoS()
|
||||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(42),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: selfDestructContract,
|
||||||
|
}), signer, testKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
stateDiff := stateDiffs[0] // state difference of block 1
|
stateDiff := stateDiffs[0] // state difference of block 1
|
||||||
|
|
@ -976,3 +1014,75 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount checks the
|
||||||
|
// content of the witness if a selfdestruct occurs in the same tx as the one that created it,
|
||||||
|
// it, but the beneficiary is the selfdestructed account. The difference with the test above,
|
||||||
|
// is that the created account is prefunded and so the final value should be 0.
|
||||||
|
func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount(t *testing.T) {
|
||||||
|
// The test txs were taken from a secondary testnet with chain id 69421
|
||||||
|
config := *testKaustinenLikeChainConfig
|
||||||
|
config.ChainID.SetUint64(69421)
|
||||||
|
|
||||||
|
var (
|
||||||
|
signer = types.LatestSigner(&config)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
gspec = verkleTestGenesis(&config)
|
||||||
|
deployer = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
contract = crypto.CreateAddress(deployer, 0)
|
||||||
|
)
|
||||||
|
// Prefund the account
|
||||||
|
gspec.Alloc[contract] = types.Account{
|
||||||
|
Balance: big.NewInt(100),
|
||||||
|
}
|
||||||
|
// The goal of this test is to test SELFDESTRUCT that happens in a contract
|
||||||
|
// execution which is created in **the same** transaction sending the remaining
|
||||||
|
// balance to itself.
|
||||||
|
t.Logf("Contract: %v", contract.String())
|
||||||
|
|
||||||
|
selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)}
|
||||||
|
|
||||||
|
_, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetPoS()
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: 0,
|
||||||
|
Value: big.NewInt(42),
|
||||||
|
Gas: 100_000,
|
||||||
|
GasPrice: big.NewInt(875000000),
|
||||||
|
Data: selfDestructContract,
|
||||||
|
}), signer, testKey)
|
||||||
|
gen.AddTx(tx)
|
||||||
|
})
|
||||||
|
stateDiff := stateDiffs[0] // state difference of block 1
|
||||||
|
|
||||||
|
{ // Check self-destructed contract in the witness
|
||||||
|
selfDestructContractTreeKey := utils.CodeHashKey(contract[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range stateDiff {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatal("no state diff found for stem")
|
||||||
|
}
|
||||||
|
balanceStateDiff := stateDiff[stateDiffIdx].SuffixDiffs[0]
|
||||||
|
if balanceStateDiff.Suffix != utils.BasicDataLeafKey {
|
||||||
|
t.Fatal("balance invalid suffix")
|
||||||
|
}
|
||||||
|
expected, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000064")
|
||||||
|
if balanceStateDiff.CurrentValue == nil || !bytes.Equal(balanceStateDiff.CurrentValue[:], expected) {
|
||||||
|
t.Fatalf("incorrect prestate balance: %x != %x", *balanceStateDiff.CurrentValue, expected)
|
||||||
|
}
|
||||||
|
// Ensure that the value is burnt, and therefore that the balance of the self-destructed
|
||||||
|
// contract isn't modified (it should remain missing from the state)
|
||||||
|
expected = make([]byte, 32)
|
||||||
|
if balanceStateDiff.NewValue == nil {
|
||||||
|
t.Fatal("incorrect nil poststate balance")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(balanceStateDiff.NewValue[:], expected[:]) {
|
||||||
|
t.Fatalf("incorrect poststate balance: %x != %x", *balanceStateDiff.NewValue, expected[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -169,10 +170,38 @@ func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) er
|
||||||
return t.root.Insert(k, v[:], t.nodeResolver)
|
return t.root.Insert(k, v[:], t.nodeResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAccount implements state.Trie, deleting the specified account from the
|
var zero32 [32]byte
|
||||||
// trie. If the account was not existent in the trie, no error will be returned.
|
|
||||||
// If the trie is corrupted, an error will be returned.
|
// DeleteAccount leaves the account untouched, as no account deletion can happen
|
||||||
|
// in verkle.
|
||||||
|
// There is a special corner case, in which an account that is prefunded, CREATE2-d
|
||||||
|
// and then SELFDESTRUCT-d should see its funds drained. EIP161 says that account
|
||||||
|
// should be removed, but this is verboten by the verkle spec. This contains a
|
||||||
|
// workaround in which the method checks for this corner case, and if so, overwrites
|
||||||
|
// the balance with 0. This will be removed once the spec has been clarified.
|
||||||
func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
||||||
|
k := utils.BasicDataKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()))
|
||||||
|
values, err := t.root.(*verkle.InternalNode).GetValuesAtStem(k, t.nodeResolver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting data at %x in delete: %w", k, err)
|
||||||
|
}
|
||||||
|
var prefunded bool
|
||||||
|
for i, v := range values {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
prefunded = len(v) == 32
|
||||||
|
case 1:
|
||||||
|
prefunded = len(v) == 32 && bytes.Equal(v, types.EmptyCodeHash[:])
|
||||||
|
default:
|
||||||
|
prefunded = v == nil
|
||||||
|
}
|
||||||
|
if !prefunded {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prefunded {
|
||||||
|
t.root.Insert(k, common.Hash{}.Bytes(), t.nodeResolver)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue