mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
Fix SELFDESTRUCT incorrect account zeroing & missing beneficiary balance in witness (#378)
* fix SELFDESTRUCT witness recording Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add selfdestruct tests Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * solve lint nit Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> --------- Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
This commit is contained in:
parent
f72f7820dd
commit
c2da1985bc
5 changed files with 513 additions and 22 deletions
|
|
@ -530,6 +530,14 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) WasCreatedInCurrentTx(addr common.Address) bool {
|
||||||
|
stateObject := s.getStateObject(addr)
|
||||||
|
if stateObject == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return stateObject.created
|
||||||
|
}
|
||||||
|
|
||||||
// SetTransientState sets transient storage for a given account. It
|
// SetTransientState sets transient storage for a given account. It
|
||||||
// adds the change to the journal so that it can be rolled back
|
// adds the change to the journal so that it can be rolled back
|
||||||
// to its previous value if there is a revert.
|
// to its previous value if there is a revert.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -896,7 +897,6 @@ func TestProcessVerklExtCodeHashOpcode(t *testing.T) {
|
||||||
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)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
contractKeccakTreeKey := utils.GetTreeKeyCodeKeccak(dummyContractAddr[:])
|
contractKeccakTreeKey := utils.GetTreeKeyCodeKeccak(dummyContractAddr[:])
|
||||||
|
|
@ -1022,3 +1022,489 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) {
|
||||||
t.Fatalf("invalid new value")
|
t.Fatalf("invalid new value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config = ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(69421),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
Ethash: new(params.EthashConfig),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
PragueTime: u64(0),
|
||||||
|
TerminalTotalDifficulty: common.Big0,
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ProofInBlocks: true,
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(config)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||||
|
gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based.
|
||||||
|
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
|
account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e")
|
||||||
|
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: config,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
coinbase: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account1: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account2: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
genesis := gspec.MustCommit(bcdb)
|
||||||
|
gspec.MustCommit(gendb)
|
||||||
|
|
||||||
|
// The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created
|
||||||
|
// in a previous transaction.
|
||||||
|
|
||||||
|
selfDestructContract := []byte{
|
||||||
|
0x60, 22, // PUSH1 22
|
||||||
|
0x60, 12, // PUSH1 12
|
||||||
|
0x60, 0x00, // PUSH1 0
|
||||||
|
0x39, // CODECOPY
|
||||||
|
|
||||||
|
0x60, 22, // PUSH1 22
|
||||||
|
0x60, 0x00, // PUSH1 0
|
||||||
|
0xF3, // RETURN
|
||||||
|
|
||||||
|
// Deployed code
|
||||||
|
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, 2, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetPoS()
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
// Create selfdestruct contract, sending 42 wei.
|
||||||
|
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
||||||
|
gen.AddTx(tx)
|
||||||
|
} else {
|
||||||
|
// Call it.
|
||||||
|
tx, _ := types.SignTx(types.NewTransaction(1, selfDestructContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey)
|
||||||
|
gen.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var zero [32]byte
|
||||||
|
{ // Check self-destructed contract in the witness
|
||||||
|
selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[1] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The new balance must be 0.
|
||||||
|
if *balanceStateDiff.NewValue != zero {
|
||||||
|
t.Fatalf("the post-state balance after self-destruct must be 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // Check self-destructed target in the witness.
|
||||||
|
selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[1] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config = ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(69421),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
Ethash: new(params.EthashConfig),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
PragueTime: u64(0),
|
||||||
|
TerminalTotalDifficulty: common.Big0,
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ProofInBlocks: true,
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(config)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||||
|
gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based.
|
||||||
|
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
|
account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e")
|
||||||
|
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: config,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
coinbase: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account1: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account2: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
genesis := gspec.MustCommit(bcdb)
|
||||||
|
gspec.MustCommit(gendb)
|
||||||
|
|
||||||
|
// 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 an external (i.e: not itself) account.
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
|
{ // Check self-destructed contract in the witness
|
||||||
|
selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[0] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // Check self-destructed target in the witness.
|
||||||
|
selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[0] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config = ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(69421),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
Ethash: new(params.EthashConfig),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
PragueTime: u64(0),
|
||||||
|
TerminalTotalDifficulty: common.Big0,
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ProofInBlocks: true,
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(config)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||||
|
gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based.
|
||||||
|
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
|
account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e")
|
||||||
|
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: config,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
coinbase: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account1: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account2: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
genesis := gspec.MustCommit(bcdb)
|
||||||
|
gspec.MustCommit(gendb)
|
||||||
|
|
||||||
|
// The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created
|
||||||
|
// in a *previous* transaction sending the remaining balance to itself.
|
||||||
|
|
||||||
|
selfDestructContract := []byte{
|
||||||
|
0x60, 22, // PUSH1 22
|
||||||
|
0x60, 12, // PUSH1 12
|
||||||
|
0x60, 0x00, // PUSH1 0
|
||||||
|
0x39, // CODECOPY
|
||||||
|
|
||||||
|
0x60, 22, // PUSH1 22
|
||||||
|
0x60, 0x00, // PUSH1 0
|
||||||
|
0xF3, // RETURN
|
||||||
|
|
||||||
|
// Deployed code
|
||||||
|
0x73, // PUSH20
|
||||||
|
0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, // 0x3a220f351252089d385b29beca14e27f204c296a
|
||||||
|
0xFF, // SELFDESTRUCT
|
||||||
|
}
|
||||||
|
selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a")
|
||||||
|
_, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetPoS()
|
||||||
|
if i == 0 {
|
||||||
|
// Create selfdestruct contract, sending 42 wei.
|
||||||
|
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey)
|
||||||
|
gen.AddTx(tx)
|
||||||
|
} else {
|
||||||
|
// Call it.
|
||||||
|
tx, _ := types.SignTx(types.NewTransaction(1, selfDestructContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey)
|
||||||
|
gen.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
{
|
||||||
|
// Check self-destructed contract in the witness.
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[1] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config = ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(69421),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
Ethash: new(params.EthashConfig),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
PragueTime: u64(0),
|
||||||
|
TerminalTotalDifficulty: common.Big0,
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ProofInBlocks: true,
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(config)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||||
|
gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based.
|
||||||
|
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
|
account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e")
|
||||||
|
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: config,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
coinbase: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account1: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
account2: GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
genesis := gspec.MustCommit(bcdb)
|
||||||
|
gspec.MustCommit(gendb)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
selfDestructContract := []byte{
|
||||||
|
0x73, // PUSH20
|
||||||
|
0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, // 0x3a220f351252089d385b29beca14e27f204c296a
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
|
{ // Check self-destructed contract in the witness
|
||||||
|
selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:])
|
||||||
|
|
||||||
|
var stateDiffIdx = -1
|
||||||
|
for i, stemStateDiff := range statediff[0] {
|
||||||
|
if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) {
|
||||||
|
stateDiffIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stateDiffIdx == -1 {
|
||||||
|
t.Fatalf("no state diff found for stem")
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1]
|
||||||
|
if balanceStateDiff.Suffix != utils.BalanceLeafKey {
|
||||||
|
t.Fatalf("balance invalid suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -943,6 +943,22 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||||
tracer.CaptureExit([]byte{}, 0, nil)
|
tracer.CaptureExit([]byte{}, 0, nil)
|
||||||
}
|
}
|
||||||
|
if interpreter.evm.chainRules.IsPrague {
|
||||||
|
contractAddr := scope.Contract.Address()
|
||||||
|
beneficiaryAddr := beneficiary.Bytes20()
|
||||||
|
// If the beneficiary isn't the contract, we need to touch the beneficiary's balance.
|
||||||
|
// If the beneficiary is the contract itself, there're two possibilities:
|
||||||
|
// 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) {
|
||||||
|
statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(beneficiaryAddr[:], uint256.Int{}, trieUtils.BalanceLeafKey)
|
||||||
|
if !scope.Contract.UseGas(statelessGas) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, errStopToken
|
return nil, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@ type StateDB interface {
|
||||||
|
|
||||||
Selfdestruct6780(common.Address)
|
Selfdestruct6780(common.Address)
|
||||||
|
|
||||||
|
WasCreatedInCurrentTx(common.Address) bool
|
||||||
|
|
||||||
// Exist reports whether the given account exists in state.
|
// Exist reports whether the given account exists in state.
|
||||||
// Notably this should also return true for self-destructed accounts.
|
// Notably this should also return true for self-destructed accounts.
|
||||||
Exist(common.Address) bool
|
Exist(common.Address) bool
|
||||||
|
|
|
||||||
|
|
@ -213,27 +213,6 @@ func (trie *VerkleTrie) UpdateStorage(address common.Address, key, value []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
||||||
var (
|
|
||||||
err error
|
|
||||||
values = make([][]byte, verkle.NodeWidth)
|
|
||||||
stem = t.pointCache.GetTreeKeyVersionCached(addr[:])
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := 0; i < verkle.NodeWidth; i++ {
|
|
||||||
values[i] = zero[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch root := t.root.(type) {
|
|
||||||
case *verkle.InternalNode:
|
|
||||||
err = root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver)
|
|
||||||
default:
|
|
||||||
return errInvalidRootType
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
|
|
||||||
}
|
|
||||||
// TODO figure out if the code size needs to be updated, too
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue