state_test_util.go 4.85 KB
Newer Older
1 2 3 4 5
package tests

import (
	"bytes"
	"fmt"
Taylor Gerring's avatar
Taylor Gerring committed
6
	"io"
7 8 9 10 11 12 13 14 15
	"math/big"
	"strconv"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethdb"
Taylor Gerring's avatar
Taylor Gerring committed
16
	"github.com/ethereum/go-ethereum/logger/glog"
17 18
)

19
func RunStateTestWithReader(r io.Reader, skipTests []string) error {
Taylor Gerring's avatar
Taylor Gerring committed
20 21 22
	tests := make(map[string]VmTest)
	if err := readJson(r, &tests); err != nil {
		return err
23
	}
24

25
	if err := runStateTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
26 27 28 29 30 31
		return err
	}

	return nil
}

32
func RunStateTest(p string, skipTests []string) error {
33
	tests := make(map[string]VmTest)
Taylor Gerring's avatar
Taylor Gerring committed
34 35 36 37
	if err := readJsonFile(p, &tests); err != nil {
		return err
	}

38
	if err := runStateTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
39 40 41 42 43 44 45
		return err
	}

	return nil

}

46 47 48
func runStateTests(tests map[string]VmTest, skipTests []string) error {
	skipTest := make(map[string]bool, len(skipTests))
	for _, name := range skipTests {
Taylor Gerring's avatar
Taylor Gerring committed
49 50
		skipTest[name] = true
	}
51 52

	for name, test := range tests {
53
		if skipTest[name] {
Taylor Gerring's avatar
Taylor Gerring committed
54
			glog.Infoln("Skipping state test", name)
55 56
			return nil
		}
57

Taylor Gerring's avatar
Taylor Gerring committed
58 59
		if err := runStateTest(test); err != nil {
			return fmt.Errorf("%s: %s\n", name, err.Error())
60 61
		}

Taylor Gerring's avatar
Taylor Gerring committed
62 63 64 65
		glog.Infoln("State test passed: ", name)
		//fmt.Println(string(statedb.Dump()))
	}
	return nil
66

Taylor Gerring's avatar
Taylor Gerring committed
67
}
68

Taylor Gerring's avatar
Taylor Gerring committed
69 70 71 72 73 74 75 76
func runStateTest(test VmTest) error {
	db, _ := ethdb.NewMemDatabase()
	statedb := state.New(common.Hash{}, db)
	for addr, account := range test.Pre {
		obj := StateObjectFromAccount(db, addr, account)
		statedb.SetStateObject(obj)
		for a, v := range account.Storage {
			obj.SetState(common.HexToHash(a), common.HexToHash(v))
77
		}
Taylor Gerring's avatar
Taylor Gerring committed
78
	}
79

Taylor Gerring's avatar
Taylor Gerring committed
80 81 82 83 84 85 86 87 88 89 90 91
	// XXX Yeah, yeah...
	env := make(map[string]string)
	env["currentCoinbase"] = test.Env.CurrentCoinbase
	env["currentDifficulty"] = test.Env.CurrentDifficulty
	env["currentGasLimit"] = test.Env.CurrentGasLimit
	env["currentNumber"] = test.Env.CurrentNumber
	env["previousHash"] = test.Env.PreviousHash
	if n, ok := test.Env.CurrentTimestamp.(float64); ok {
		env["currentTimestamp"] = strconv.Itoa(int(n))
	} else {
		env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
	}
92

Taylor Gerring's avatar
Taylor Gerring committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106
	var (
		ret []byte
		// gas  *big.Int
		// err  error
		logs state.Logs
	)

	ret, logs, _, _ = RunState(statedb, env, test.Transaction)

	// // Compare expected  and actual return
	rexp := common.FromHex(test.Out)
	if bytes.Compare(rexp, ret) != 0 {
		return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
	}
107

Taylor Gerring's avatar
Taylor Gerring committed
108 109 110 111 112 113
	// check post state
	for addr, account := range test.Post {
		obj := statedb.GetStateObject(common.HexToAddress(addr))
		if obj == nil {
			continue
		}
114

Taylor Gerring's avatar
Taylor Gerring committed
115 116
		if obj.Balance().Cmp(common.Big(account.Balance)) != 0 {
			return fmt.Errorf("(%x) balance failed. Expected %v, got %v => %v\n", obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance()))
117 118
		}

Taylor Gerring's avatar
Taylor Gerring committed
119 120
		if obj.Nonce() != common.String2Big(account.Nonce).Uint64() {
			return fmt.Errorf("(%x) nonce failed. Expected %v, got %v\n", obj.Address().Bytes()[:4], account.Nonce, obj.Nonce())
121 122
		}

Taylor Gerring's avatar
Taylor Gerring committed
123
		for addr, value := range account.Storage {
Taylor Gerring's avatar
Taylor Gerring committed
124 125
			v := obj.GetState(common.HexToHash(addr))
			vexp := common.HexToHash(value)
Taylor Gerring's avatar
Taylor Gerring committed
126

Taylor Gerring's avatar
Taylor Gerring committed
127 128
			if v != vexp {
				return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, vexp.Big(), v.Big())
Taylor Gerring's avatar
Taylor Gerring committed
129 130
			}
		}
Taylor Gerring's avatar
Taylor Gerring committed
131
	}
132

Taylor Gerring's avatar
Taylor Gerring committed
133 134 135
	statedb.Sync()
	if common.HexToHash(test.PostStateRoot) != statedb.Root() {
		return fmt.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, statedb.Root())
Taylor Gerring's avatar
Taylor Gerring committed
136
	}
Taylor Gerring's avatar
Taylor Gerring committed
137 138 139 140 141 142 143 144

	// check logs
	if len(test.Logs) > 0 {
		if err := checkLogs(test.Logs, logs); err != nil {
			return err
		}
	}

Taylor Gerring's avatar
Taylor Gerring committed
145
	return nil
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
}

func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.Logs, *big.Int, error) {
	var (
		keyPair, _ = crypto.NewKeyPairFromSec([]byte(common.Hex2Bytes(tx["secretKey"])))
		data       = common.FromHex(tx["data"])
		gas        = common.Big(tx["gasLimit"])
		price      = common.Big(tx["gasPrice"])
		value      = common.Big(tx["value"])
		nonce      = common.Big(tx["nonce"]).Uint64()
		caddr      = common.HexToAddress(env["currentCoinbase"])
	)

	var to *common.Address
	if len(tx["to"]) > 2 {
		t := common.HexToAddress(tx["to"])
		to = &t
	}
	// Set pre compiled contracts
	vm.Precompiled = vm.PrecompiledContracts()

	snapshot := statedb.Copy()
	coinbase := statedb.GetOrNewStateObject(caddr)
obscuren's avatar
obscuren committed
169
	coinbase.SetGasLimit(common.Big(env["currentGasLimit"]))
170 171 172 173 174

	message := NewMessage(common.BytesToAddress(keyPair.Address()), to, data, value, gas, price, nonce)
	vmenv := NewEnvFromMap(statedb, env, tx)
	vmenv.origin = common.BytesToAddress(keyPair.Address())
	ret, _, err := core.ApplyMessage(vmenv, message, coinbase)
Taylor Gerring's avatar
Taylor Gerring committed
175
	if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || state.IsGasLimitErr(err) {
176 177 178 179 180 181
		statedb.Set(snapshot)
	}
	statedb.Update()

	return ret, vmenv.state.Logs(), vmenv.Gas, err
}