|
|
|
@ -2376,11 +2376,9 @@ func TestDeleteRecreateSlots(t *testing.T) { |
|
|
|
|
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(1000000000) |
|
|
|
|
|
|
|
|
|
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43") |
|
|
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
|
|
|
|
address = crypto.PubkeyToAddress(key.PublicKey) |
|
|
|
|
funds = big.NewInt(1000000000) |
|
|
|
|
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)
|
|
|
|
@ -2403,7 +2401,7 @@ func TestDeleteRecreateSlots(t *testing.T) { |
|
|
|
|
byte(vm.PUSH1), 0x4, // location
|
|
|
|
|
byte(vm.SSTORE), // Set slot[4] = 1
|
|
|
|
|
// Slots are set, now return the code
|
|
|
|
|
byte(vm.PUSH2), 0x88, 0xff, // Push code on stack
|
|
|
|
|
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.
|
|
|
|
@ -2428,6 +2426,10 @@ func TestDeleteRecreateSlots(t *testing.T) { |
|
|
|
|
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{ |
|
|
|
@ -2563,3 +2565,196 @@ func TestDeleteRecreateAccount(t *testing.T) { |
|
|
|
|
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(1000000000) |
|
|
|
|
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) *types.Transaction { |
|
|
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, aa, |
|
|
|
|
big.NewInt(0), 50000, big.NewInt(1), 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) *types.Transaction { |
|
|
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, bb, |
|
|
|
|
big.NewInt(0), 100000, big.NewInt(1), 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)) |
|
|
|
|
} |
|
|
|
|
if i%3 == 0 { |
|
|
|
|
b.AddTx(newResurrect(exp)) |
|
|
|
|
} |
|
|
|
|
if i%5 == 0 { |
|
|
|
|
b.AddTx(newDestruct(exp)) |
|
|
|
|
} |
|
|
|
|
if i%7 == 0 { |
|
|
|
|
b.AddTx(newResurrect(exp)) |
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|