Selaa lähdekoodia

core: more blockchain tests

Martin Holst Swende 5 vuotta sitten
vanhempi
commit
bc5d742c66
1 muutettua tiedostoa jossa 201 lisäystä ja 6 poistoa
  1. 201 6
      core/blockchain_test.go

+ 201 - 6
core/blockchain_test.go

@@ -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)
+			}
+		}
+	}
+}