mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
feat: disable SELFDESTRUCT opcode in l2geth (#147)
* feat: disable SELFDESTRUCT opcode in l2geth * remove deadcode
This commit is contained in:
parent
01a0618b02
commit
00ed553a57
7 changed files with 69 additions and 475 deletions
|
|
@ -415,16 +415,17 @@ func TestTransactionByHash(t *testing.T) {
|
||||||
|
|
||||||
func TestEstimateGas(t *testing.T) {
|
func TestEstimateGas(t *testing.T) {
|
||||||
/*
|
/*
|
||||||
pragma solidity ^0.6.4;
|
pragma solidity =0.6.12;
|
||||||
contract GasEstimation {
|
contract GasEstimation {
|
||||||
function PureRevert() public { revert(); }
|
function PureRevert() public { revert(); }
|
||||||
function Revert() public { revert("revert reason");}
|
function Revert() public { revert("revert reason");}
|
||||||
function OOG() public { for (uint i = 0; ; i++) {}}
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||||
function Assert() public { assert(false);}
|
function Assert() public { assert(false);}
|
||||||
|
function SelfDestruct() public { selfdestruct(msg.sender); }
|
||||||
function Valid() public {}
|
function Valid() public {}
|
||||||
}*/
|
}*/
|
||||||
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SelfDestruct\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
const contractBin = "0x608060405234801561001057600080fd5b50610181806100206000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806350f6fe3414610067578063aa8b1d3014610071578063b9b046f91461007b578063d8b9839114610085578063dba5e9171461008f578063e09fface14610099575b600080fd5b61006f6100a3565b005b6100796100b3565b005b6100836100b8565b005b61008d6100c2565b005b610097610130565b005b6100a1610149565b005b60005b80806001019150506100a6565b600080fd5b60006100c057fe5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16ff5b56fea2646970667358221220381c1672ed4be389ed2337c96f8e2fccf83479ec05cb9dd10362e8819e7ca38f64736f6c634300060c0033"
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
@ -498,6 +499,15 @@ func TestEstimateGas(t *testing.T) {
|
||||||
Data: common.Hex2Bytes("b9b046f9"),
|
Data: common.Hex2Bytes("b9b046f9"),
|
||||||
}, 0, errors.New("invalid opcode: INVALID"), nil},
|
}, 0, errors.New("invalid opcode: INVALID"), nil},
|
||||||
|
|
||||||
|
{"SelfDestruct", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("dba5e917"),
|
||||||
|
}, 0, errors.New("invalid opcode: SELFDESTRUCT"), nil},
|
||||||
|
|
||||||
{"Valid", ethereum.CallMsg{
|
{"Valid", ethereum.CallMsg{
|
||||||
From: addr,
|
From: addr,
|
||||||
To: &contractAddr,
|
To: &contractAddr,
|
||||||
|
|
@ -505,7 +515,7 @@ func TestEstimateGas(t *testing.T) {
|
||||||
GasPrice: big.NewInt(0),
|
GasPrice: big.NewInt(0),
|
||||||
Value: nil,
|
Value: nil,
|
||||||
Data: common.Hex2Bytes("e09fface"),
|
Data: common.Hex2Bytes("e09fface"),
|
||||||
}, 21275, nil, nil},
|
}, 21296, nil, nil},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
|
|
|
||||||
|
|
@ -2511,6 +2511,52 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSelfDestructDisabled
|
||||||
|
// If a transaction's execution triggers SELFDESTRUCT, it will fail.
|
||||||
|
func TestSelfDestructDisabled(t *testing.T) {
|
||||||
|
var (
|
||||||
|
// Generate a canonical chain to act as the main dataset
|
||||||
|
engine = ethash.NewFaker()
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
// A sender who makes transactions, has some funds
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = big.NewInt(1000000000000000)
|
||||||
|
|
||||||
|
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||||
|
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
||||||
|
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
||||||
|
)
|
||||||
|
|
||||||
|
gspec := &Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
address: {Balance: funds},
|
||||||
|
// The address 0xAAAAA selfdestructs if called
|
||||||
|
aa: {
|
||||||
|
// Code needs to just selfdestruct
|
||||||
|
Code: aaCode,
|
||||||
|
Nonce: 1,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
Storage: aaStorage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
|
||||||
|
_, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) {
|
||||||
|
b.SetCoinbase(common.Address{1})
|
||||||
|
// One transaction to AA, to kill it
|
||||||
|
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
||||||
|
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
})
|
||||||
|
|
||||||
|
if receipts[0][0].Status != types.ReceiptStatusFailed {
|
||||||
|
t.Fatalf("Expected transaction triggering SELFDESTRUCT to fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
||||||
// while changing the internals of statedb. The workflow is that a contract is
|
// while changing the internals of statedb. The workflow is that a contract is
|
||||||
// self destructed, then in a followup transaction (but same block) it's created
|
// self destructed, then in a followup transaction (but same block) it's created
|
||||||
|
|
@ -2586,402 +2632,6 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDeleteRecreateSlots tests a state-transition that contains both deletion
|
|
||||||
// and recreation of contract state.
|
|
||||||
// Contract A exists, has slots 1 and 2 set
|
|
||||||
// Tx 1: Selfdestruct A
|
|
||||||
// Tx 2: Re-create A, set slots 3 and 4
|
|
||||||
// Expected outcome is that _all_ slots are cleared from A, due to the selfdestruct,
|
|
||||||
// and then the new slots exist
|
|
||||||
func TestDeleteRecreateSlots(t *testing.T) {
|
|
||||||
var (
|
|
||||||
// Generate a canonical chain to act as the main dataset
|
|
||||||
engine = ethash.NewFaker()
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
|
||||||
// A sender who makes transactions, has some funds
|
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
funds = big.NewInt(1000000000000000)
|
|
||||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
||||||
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
||||||
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
||||||
)
|
|
||||||
// Populate two slots
|
|
||||||
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
||||||
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
||||||
|
|
||||||
// The bb-code needs to CREATE2 the aa contract. It consists of
|
|
||||||
// both initcode and deployment code
|
|
||||||
// initcode:
|
|
||||||
// 1. Set slots 3=3, 4=4,
|
|
||||||
// 2. Return aaCode
|
|
||||||
|
|
||||||
initCode := []byte{
|
|
||||||
byte(vm.PUSH1), 0x3, // value
|
|
||||||
byte(vm.PUSH1), 0x3, // location
|
|
||||||
byte(vm.SSTORE), // Set slot[3] = 1
|
|
||||||
byte(vm.PUSH1), 0x4, // value
|
|
||||||
byte(vm.PUSH1), 0x4, // location
|
|
||||||
byte(vm.SSTORE), // Set slot[4] = 1
|
|
||||||
// Slots are set, now return the code
|
|
||||||
byte(vm.PUSH2), byte(vm.PC), byte(vm.SELFDESTRUCT), // Push code on stack
|
|
||||||
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
||||||
byte(vm.MSTORE),
|
|
||||||
// Code is now in memory.
|
|
||||||
byte(vm.PUSH1), 0x2, // size
|
|
||||||
byte(vm.PUSH1), byte(32 - 2), // offset
|
|
||||||
byte(vm.RETURN),
|
|
||||||
}
|
|
||||||
if l := len(initCode); l > 32 {
|
|
||||||
t.Fatalf("init code is too long for a pushx, need a more elaborate deployer")
|
|
||||||
}
|
|
||||||
bbCode := []byte{
|
|
||||||
// Push initcode onto stack
|
|
||||||
byte(vm.PUSH1) + byte(len(initCode)-1)}
|
|
||||||
bbCode = append(bbCode, initCode...)
|
|
||||||
bbCode = append(bbCode, []byte{
|
|
||||||
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
||||||
byte(vm.MSTORE),
|
|
||||||
byte(vm.PUSH1), 0x00, // salt
|
|
||||||
byte(vm.PUSH1), byte(len(initCode)), // size
|
|
||||||
byte(vm.PUSH1), byte(32 - len(initCode)), // offset
|
|
||||||
byte(vm.PUSH1), 0x00, // endowment
|
|
||||||
byte(vm.CREATE2),
|
|
||||||
}...)
|
|
||||||
|
|
||||||
initHash := crypto.Keccak256Hash(initCode)
|
|
||||||
aa := crypto.CreateAddress2(bb, [32]byte{}, initHash[:])
|
|
||||||
t.Logf("Destination address: %x\n", aa)
|
|
||||||
|
|
||||||
gspec := &Genesis{
|
|
||||||
Config: params.TestChainConfig,
|
|
||||||
Alloc: GenesisAlloc{
|
|
||||||
address: {Balance: funds},
|
|
||||||
// The address 0xAAAAA selfdestructs if called
|
|
||||||
aa: {
|
|
||||||
// Code needs to just selfdestruct
|
|
||||||
Code: aaCode,
|
|
||||||
Nonce: 1,
|
|
||||||
Balance: big.NewInt(0),
|
|
||||||
Storage: aaStorage,
|
|
||||||
},
|
|
||||||
// The contract BB recreates AA
|
|
||||||
bb: {
|
|
||||||
Code: bbCode,
|
|
||||||
Balance: big.NewInt(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
genesis := gspec.MustCommit(db)
|
|
||||||
|
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) {
|
|
||||||
b.SetCoinbase(common.Address{1})
|
|
||||||
// One transaction to AA, to kill it
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
||||||
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
b.AddTx(tx)
|
|
||||||
// One transaction to BB, to recreate AA
|
|
||||||
tx, _ = types.SignTx(types.NewTransaction(1, bb,
|
|
||||||
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
b.AddTx(tx)
|
|
||||||
})
|
|
||||||
// Import the canonical chain
|
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
|
||||||
gspec.MustCommit(diskdb)
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
|
||||||
Debug: true,
|
|
||||||
Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
if n, err := chain.InsertChain(blocks); err != nil {
|
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
||||||
}
|
|
||||||
statedb, _ := chain.State()
|
|
||||||
|
|
||||||
// If all is correct, then slot 1 and 2 are zero
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
// Also, 3 and 4 should be set
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("03")), common.HexToHash("03"); got != exp {
|
|
||||||
t.Fatalf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("04")), common.HexToHash("04"); got != exp {
|
|
||||||
t.Fatalf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDeleteRecreateAccount tests a state-transition that contains deletion of a
|
|
||||||
// contract with storage, and a recreate of the same contract via a
|
|
||||||
// regular value-transfer
|
|
||||||
// Expected outcome is that _all_ slots are cleared from A
|
|
||||||
func TestDeleteRecreateAccount(t *testing.T) {
|
|
||||||
var (
|
|
||||||
// Generate a canonical chain to act as the main dataset
|
|
||||||
engine = ethash.NewFaker()
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
|
||||||
// A sender who makes transactions, has some funds
|
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
funds = big.NewInt(1000000000000000)
|
|
||||||
|
|
||||||
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
|
||||||
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
||||||
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
||||||
)
|
|
||||||
// Populate two slots
|
|
||||||
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
||||||
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
||||||
|
|
||||||
gspec := &Genesis{
|
|
||||||
Config: params.TestChainConfig,
|
|
||||||
Alloc: GenesisAlloc{
|
|
||||||
address: {Balance: funds},
|
|
||||||
// The address 0xAAAAA selfdestructs if called
|
|
||||||
aa: {
|
|
||||||
// Code needs to just selfdestruct
|
|
||||||
Code: aaCode,
|
|
||||||
Nonce: 1,
|
|
||||||
Balance: big.NewInt(0),
|
|
||||||
Storage: aaStorage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
genesis := gspec.MustCommit(db)
|
|
||||||
|
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) {
|
|
||||||
b.SetCoinbase(common.Address{1})
|
|
||||||
// One transaction to AA, to kill it
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
||||||
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
b.AddTx(tx)
|
|
||||||
// One transaction to AA, to recreate it (but without storage
|
|
||||||
tx, _ = types.SignTx(types.NewTransaction(1, aa,
|
|
||||||
big.NewInt(1), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
b.AddTx(tx)
|
|
||||||
})
|
|
||||||
// Import the canonical chain
|
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
|
||||||
gspec.MustCommit(diskdb)
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
|
||||||
Debug: true,
|
|
||||||
Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
if n, err := chain.InsertChain(blocks); err != nil {
|
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
||||||
}
|
|
||||||
statedb, _ := chain.State()
|
|
||||||
|
|
||||||
// If all is correct, then both slots are zero
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("got %x exp %x", got, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDeleteRecreateSlotsAcrossManyBlocks tests multiple state-transition that contains both deletion
|
|
||||||
// and recreation of contract state.
|
|
||||||
// Contract A exists, has slots 1 and 2 set
|
|
||||||
// Tx 1: Selfdestruct A
|
|
||||||
// Tx 2: Re-create A, set slots 3 and 4
|
|
||||||
// Expected outcome is that _all_ slots are cleared from A, due to the selfdestruct,
|
|
||||||
// and then the new slots exist
|
|
||||||
func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
|
|
||||||
var (
|
|
||||||
// Generate a canonical chain to act as the main dataset
|
|
||||||
engine = ethash.NewFaker()
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
|
||||||
// A sender who makes transactions, has some funds
|
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
funds = big.NewInt(1000000000000000)
|
|
||||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
||||||
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
||||||
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
||||||
)
|
|
||||||
// Populate two slots
|
|
||||||
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
||||||
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
||||||
|
|
||||||
// The bb-code needs to CREATE2 the aa contract. It consists of
|
|
||||||
// both initcode and deployment code
|
|
||||||
// initcode:
|
|
||||||
// 1. Set slots 3=blocknum+1, 4=4,
|
|
||||||
// 2. Return aaCode
|
|
||||||
|
|
||||||
initCode := []byte{
|
|
||||||
byte(vm.PUSH1), 0x1, //
|
|
||||||
byte(vm.NUMBER), // value = number + 1
|
|
||||||
byte(vm.ADD), //
|
|
||||||
byte(vm.PUSH1), 0x3, // location
|
|
||||||
byte(vm.SSTORE), // Set slot[3] = number + 1
|
|
||||||
byte(vm.PUSH1), 0x4, // value
|
|
||||||
byte(vm.PUSH1), 0x4, // location
|
|
||||||
byte(vm.SSTORE), // Set slot[4] = 4
|
|
||||||
// Slots are set, now return the code
|
|
||||||
byte(vm.PUSH2), byte(vm.PC), byte(vm.SELFDESTRUCT), // Push code on stack
|
|
||||||
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
||||||
byte(vm.MSTORE),
|
|
||||||
// Code is now in memory.
|
|
||||||
byte(vm.PUSH1), 0x2, // size
|
|
||||||
byte(vm.PUSH1), byte(32 - 2), // offset
|
|
||||||
byte(vm.RETURN),
|
|
||||||
}
|
|
||||||
if l := len(initCode); l > 32 {
|
|
||||||
t.Fatalf("init code is too long for a pushx, need a more elaborate deployer")
|
|
||||||
}
|
|
||||||
bbCode := []byte{
|
|
||||||
// Push initcode onto stack
|
|
||||||
byte(vm.PUSH1) + byte(len(initCode)-1)}
|
|
||||||
bbCode = append(bbCode, initCode...)
|
|
||||||
bbCode = append(bbCode, []byte{
|
|
||||||
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
||||||
byte(vm.MSTORE),
|
|
||||||
byte(vm.PUSH1), 0x00, // salt
|
|
||||||
byte(vm.PUSH1), byte(len(initCode)), // size
|
|
||||||
byte(vm.PUSH1), byte(32 - len(initCode)), // offset
|
|
||||||
byte(vm.PUSH1), 0x00, // endowment
|
|
||||||
byte(vm.CREATE2),
|
|
||||||
}...)
|
|
||||||
|
|
||||||
initHash := crypto.Keccak256Hash(initCode)
|
|
||||||
aa := crypto.CreateAddress2(bb, [32]byte{}, initHash[:])
|
|
||||||
t.Logf("Destination address: %x\n", aa)
|
|
||||||
gspec := &Genesis{
|
|
||||||
Config: params.TestChainConfig,
|
|
||||||
Alloc: GenesisAlloc{
|
|
||||||
address: {Balance: funds},
|
|
||||||
// The address 0xAAAAA selfdestructs if called
|
|
||||||
aa: {
|
|
||||||
// Code needs to just selfdestruct
|
|
||||||
Code: aaCode,
|
|
||||||
Nonce: 1,
|
|
||||||
Balance: big.NewInt(0),
|
|
||||||
Storage: aaStorage,
|
|
||||||
},
|
|
||||||
// The contract BB recreates AA
|
|
||||||
bb: {
|
|
||||||
Code: bbCode,
|
|
||||||
Balance: big.NewInt(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
genesis := gspec.MustCommit(db)
|
|
||||||
var nonce uint64
|
|
||||||
|
|
||||||
type expectation struct {
|
|
||||||
exist bool
|
|
||||||
blocknum int
|
|
||||||
values map[int]int
|
|
||||||
}
|
|
||||||
var current = &expectation{
|
|
||||||
exist: true, // exists in genesis
|
|
||||||
blocknum: 0,
|
|
||||||
values: map[int]int{1: 1, 2: 2},
|
|
||||||
}
|
|
||||||
var expectations []*expectation
|
|
||||||
var newDestruct = func(e *expectation, b *BlockGen) *types.Transaction {
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(nonce, aa,
|
|
||||||
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
nonce++
|
|
||||||
if e.exist {
|
|
||||||
e.exist = false
|
|
||||||
e.values = nil
|
|
||||||
}
|
|
||||||
t.Logf("block %d; adding destruct\n", e.blocknum)
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
var newResurrect = func(e *expectation, b *BlockGen) *types.Transaction {
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(nonce, bb,
|
|
||||||
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
||||||
nonce++
|
|
||||||
if !e.exist {
|
|
||||||
e.exist = true
|
|
||||||
e.values = map[int]int{3: e.blocknum + 1, 4: 4}
|
|
||||||
}
|
|
||||||
t.Logf("block %d; adding resurrect\n", e.blocknum)
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 150, func(i int, b *BlockGen) {
|
|
||||||
var exp = new(expectation)
|
|
||||||
exp.blocknum = i + 1
|
|
||||||
exp.values = make(map[int]int)
|
|
||||||
for k, v := range current.values {
|
|
||||||
exp.values[k] = v
|
|
||||||
}
|
|
||||||
exp.exist = current.exist
|
|
||||||
|
|
||||||
b.SetCoinbase(common.Address{1})
|
|
||||||
if i%2 == 0 {
|
|
||||||
b.AddTx(newDestruct(exp, b))
|
|
||||||
}
|
|
||||||
if i%3 == 0 {
|
|
||||||
b.AddTx(newResurrect(exp, b))
|
|
||||||
}
|
|
||||||
if i%5 == 0 {
|
|
||||||
b.AddTx(newDestruct(exp, b))
|
|
||||||
}
|
|
||||||
if i%7 == 0 {
|
|
||||||
b.AddTx(newResurrect(exp, b))
|
|
||||||
}
|
|
||||||
expectations = append(expectations, exp)
|
|
||||||
current = exp
|
|
||||||
})
|
|
||||||
// Import the canonical chain
|
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
|
||||||
gspec.MustCommit(diskdb)
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
|
||||||
//Debug: true,
|
|
||||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
var asHash = func(num int) common.Hash {
|
|
||||||
return common.BytesToHash([]byte{byte(num)})
|
|
||||||
}
|
|
||||||
for i, block := range blocks {
|
|
||||||
blockNum := i + 1
|
|
||||||
if n, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
||||||
}
|
|
||||||
statedb, _ := chain.State()
|
|
||||||
// If all is correct, then slot 1 and 2 are zero
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("block %d, got %x exp %x", blockNum, got, exp)
|
|
||||||
}
|
|
||||||
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
||||||
t.Errorf("block %d, got %x exp %x", blockNum, got, exp)
|
|
||||||
}
|
|
||||||
exp := expectations[i]
|
|
||||||
if exp.exist {
|
|
||||||
if !statedb.Exist(aa) {
|
|
||||||
t.Fatalf("block %d, expected %v to exist, it did not", blockNum, aa)
|
|
||||||
}
|
|
||||||
for slot, val := range exp.values {
|
|
||||||
if gotValue, expValue := statedb.GetState(aa, asHash(slot)), asHash(val); gotValue != expValue {
|
|
||||||
t.Fatalf("block %d, slot %d, got %x exp %x", blockNum, slot, gotValue, expValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if statedb.Exist(aa) {
|
|
||||||
t.Fatalf("block %d, expected %v to not exist, it did", blockNum, aa)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestInitThenFailCreateContract tests a pretty notorious case that happened
|
// TestInitThenFailCreateContract tests a pretty notorious case that happened
|
||||||
// on mainnet over blocks 7338108, 7338110 and 7338115.
|
// on mainnet over blocks 7338108, 7338110 and 7338115.
|
||||||
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
|
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
|
||||||
|
|
|
||||||
|
|
@ -142,10 +142,9 @@ func enable2929(jt *JumpTable) {
|
||||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||||
|
|
||||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
// SELFDESTRUCT is disabled in Scroll
|
||||||
// factor here
|
// jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
|
||||||
jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
|
// jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
|
||||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable3529 enabled "EIP-3529: Reduction in refunds":
|
// enable3529 enabled "EIP-3529: Reduction in refunds":
|
||||||
|
|
@ -154,7 +153,9 @@ func enable2929(jt *JumpTable) {
|
||||||
// - Reduces max refunds to 20% gas
|
// - Reduces max refunds to 20% gas
|
||||||
func enable3529(jt *JumpTable) {
|
func enable3529(jt *JumpTable) {
|
||||||
jt[SSTORE].dynamicGas = gasSStoreEIP3529
|
jt[SSTORE].dynamicGas = gasSStoreEIP3529
|
||||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
|
|
||||||
|
// SELFDESTRUCT is disabled in Scroll
|
||||||
|
// jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||||
|
|
|
||||||
|
|
@ -416,26 +416,3 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
var gas uint64
|
|
||||||
// EIP150 homestead gas reprice fork:
|
|
||||||
if evm.chainRules.IsEIP150 {
|
|
||||||
gas = params.SelfdestructGasEIP150
|
|
||||||
var address = common.Address(stack.Back(0).Bytes20())
|
|
||||||
|
|
||||||
if evm.chainRules.IsEIP158 {
|
|
||||||
// if empty and transfers value
|
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
|
||||||
gas += params.CreateBySelfdestructGas
|
|
||||||
}
|
|
||||||
} else if !evm.StateDB.Exist(address) {
|
|
||||||
gas += params.CreateBySelfdestructGas
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
|
||||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -787,18 +787,6 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
beneficiary := scope.Stack.pop()
|
|
||||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
|
||||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
|
|
||||||
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
|
|
||||||
if interpreter.cfg.Debug {
|
|
||||||
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
|
||||||
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// following functions are used by the instruction jump table
|
// following functions are used by the instruction jump table
|
||||||
|
|
||||||
// make log instruction function
|
// make log instruction function
|
||||||
|
|
|
||||||
|
|
@ -1021,13 +1021,8 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
memorySize: memoryReturn,
|
memorySize: memoryReturn,
|
||||||
halts: true,
|
halts: true,
|
||||||
},
|
},
|
||||||
SELFDESTRUCT: {
|
// SELFDESTRUCT is disabled in Scroll.
|
||||||
execute: opSuicide,
|
// SELFDESTRUCT has the same behavior as INVALID.
|
||||||
dynamicGas: gasSelfdestruct,
|
SELFDESTRUCT: nil,
|
||||||
minStack: minStack(1, 0),
|
|
||||||
maxStack: maxStack(1, 0),
|
|
||||||
halts: true,
|
|
||||||
writes: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,9 +196,6 @@ var (
|
||||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
|
||||||
// gasSelfdestructEIP3529 implements the changes in EIP-2539 (no refunds)
|
|
||||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
|
||||||
|
|
||||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
||||||
//
|
//
|
||||||
|
|
@ -218,27 +215,3 @@ var (
|
||||||
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
||||||
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||||||
)
|
)
|
||||||
|
|
||||||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
|
|
||||||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|
||||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
var (
|
|
||||||
gas uint64
|
|
||||||
address = common.Address(stack.peek().Bytes20())
|
|
||||||
)
|
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
|
||||||
gas = params.ColdAccountAccessCostEIP2929
|
|
||||||
}
|
|
||||||
// if empty and transfers value
|
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
|
||||||
gas += params.CreateBySelfdestructGas
|
|
||||||
}
|
|
||||||
if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
|
|
||||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
return gasFunc
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue