|
|
@@ -2287,3 +2287,78 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
|
|
t.Errorf("Got error, %v", err)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
|
|
+// 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
|
|
|
+// again and the transaction reverted.
|
|
|
+//
|
|
|
+// The original statedb implementation flushed dirty objects to the tries after
|
|
|
+// each transaction, so this works ok. The rework accumulated writes in memory
|
|
|
+// first, but the journal wiped the entire state object on create-revert.
|
|
|
+func TestDeleteCreateRevert(t *testing.T) {
|
|
|
+ var (
|
|
|
+ aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
|
+ bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
|
+ // 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)
|
|
|
+ gspec = &Genesis{
|
|
|
+ Config: params.TestChainConfig,
|
|
|
+ Alloc: GenesisAlloc{
|
|
|
+ address: {Balance: funds},
|
|
|
+ // The address 0xAAAAA selfdestructs if called
|
|
|
+ aa: {
|
|
|
+ // Code needs to just selfdestruct
|
|
|
+ Code: []byte{byte(vm.PC), 0xFF},
|
|
|
+ Nonce: 1,
|
|
|
+ Balance: big.NewInt(0),
|
|
|
+ },
|
|
|
+ // The address 0xBBBB send 1 wei to 0xAAAA, then reverts
|
|
|
+ bb: {
|
|
|
+ Code: []byte{
|
|
|
+ byte(vm.PC), // [0]
|
|
|
+ byte(vm.DUP1), // [0,0]
|
|
|
+ byte(vm.DUP1), // [0,0,0]
|
|
|
+ byte(vm.DUP1), // [0,0,0,0]
|
|
|
+ byte(vm.PUSH1), 0x01, // [0,0,0,0,1] (value)
|
|
|
+ byte(vm.PUSH2), 0xaa, 0xaa, // [0,0,0,0,1, 0xaaaa]
|
|
|
+ byte(vm.GAS),
|
|
|
+ byte(vm.CALL),
|
|
|
+ byte(vm.REVERT),
|
|
|
+ },
|
|
|
+ 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 AAAA
|
|
|
+ tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
|
+ big.NewInt(0), 50000, big.NewInt(1), nil), types.HomesteadSigner{}, key)
|
|
|
+ b.AddTx(tx)
|
|
|
+ // One transaction to BBBB
|
|
|
+ tx, _ = types.SignTx(types.NewTransaction(1, bb,
|
|
|
+ big.NewInt(0), 100000, big.NewInt(1), 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{}, 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)
|
|
|
+ }
|
|
|
+}
|