vm_test_util.go 3.96 KB
Newer Older
1
package tests
obscuren's avatar
obscuren committed
2 3 4

import (
	"bytes"
5
	"fmt"
Taylor Gerring's avatar
Taylor Gerring committed
6
	"io"
obscuren's avatar
obscuren committed
7 8
	"math/big"
	"strconv"
obscuren's avatar
obscuren committed
9

obscuren's avatar
obscuren committed
10
	"github.com/ethereum/go-ethereum/common"
11
	"github.com/ethereum/go-ethereum/core/state"
12
	"github.com/ethereum/go-ethereum/core/vm"
obscuren's avatar
obscuren committed
13
	"github.com/ethereum/go-ethereum/ethdb"
Taylor Gerring's avatar
Taylor Gerring committed
14
	"github.com/ethereum/go-ethereum/logger/glog"
obscuren's avatar
obscuren committed
15 16
)

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

Taylor Gerring's avatar
Taylor Gerring committed
24 25 26 27
	if err != nil {
		return err
	}

28
	if err := runVmTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
29 30 31 32 33 34
		return err
	}

	return nil
}

35
func RunVmTest(p string, skipTests []string) error {
Taylor Gerring's avatar
Taylor Gerring committed
36

obscuren's avatar
obscuren committed
37
	tests := make(map[string]VmTest)
Taylor Gerring's avatar
Taylor Gerring committed
38
	err := readJsonFile(p, &tests)
39 40 41
	if err != nil {
		return err
	}
obscuren's avatar
obscuren committed
42

43
	if err := runVmTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
44 45 46 47 48 49
		return err
	}

	return nil
}

50 51 52
func runVmTests(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
53 54 55
		skipTest[name] = true
	}

obscuren's avatar
obscuren committed
56
	for name, test := range tests {
57
		if skipTest[name] {
Taylor Gerring's avatar
Taylor Gerring committed
58
			glog.Infoln("Skipping VM test", name)
59 60
			return nil
		}
Taylor Gerring's avatar
Taylor Gerring committed
61 62 63

		if err := runVmTest(test); err != nil {
			return fmt.Errorf("%s %s", name, err.Error())
obscuren's avatar
obscuren committed
64 65
		}

Taylor Gerring's avatar
Taylor Gerring committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79
		glog.Infoln("VM test passed: ", name)
		//fmt.Println(string(statedb.Dump()))
	}
	return nil
}

func runVmTest(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))
obscuren's avatar
obscuren committed
80
		}
Taylor Gerring's avatar
Taylor Gerring committed
81
	}
obscuren's avatar
obscuren committed
82

Taylor Gerring's avatar
Taylor Gerring committed
83 84 85 86 87 88 89 90 91 92 93 94
	// 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)
	}
obscuren's avatar
obscuren committed
95

Taylor Gerring's avatar
Taylor Gerring committed
96 97 98 99 100 101
	var (
		ret  []byte
		gas  *big.Int
		err  error
		logs state.Logs
	)
obscuren's avatar
obscuren committed
102

Taylor Gerring's avatar
Taylor Gerring committed
103
	ret, logs, gas, err = RunVm(statedb, env, test.Exec)
obscuren's avatar
obscuren committed
104

Taylor Gerring's avatar
Taylor Gerring committed
105
	// Compare expected and actual return
Taylor Gerring's avatar
Taylor Gerring committed
106 107 108 109 110 111 112 113 114 115 116 117
	rexp := common.FromHex(test.Out)
	if bytes.Compare(rexp, ret) != 0 {
		return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
	}

	// Check gas usage
	if len(test.Gas) == 0 && err == nil {
		return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successfull")
	} else {
		gexp := common.Big(test.Gas)
		if gexp.Cmp(gas) != 0 {
			return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas)
obscuren's avatar
obscuren committed
118
		}
Taylor Gerring's avatar
Taylor Gerring committed
119
	}
obscuren's avatar
obscuren committed
120

Taylor Gerring's avatar
Taylor Gerring committed
121 122 123 124 125 126
	// check post state
	for addr, account := range test.Post {
		obj := statedb.GetStateObject(common.HexToAddress(addr))
		if obj == nil {
			continue
		}
obscuren's avatar
obscuren committed
127

Taylor Gerring's avatar
Taylor Gerring committed
128 129 130
		for addr, value := range account.Storage {
			v := obj.GetState(common.HexToHash(addr))
			vexp := common.HexToHash(value)
obscuren's avatar
obscuren committed
131

Taylor Gerring's avatar
Taylor Gerring committed
132
			if v != vexp {
Taylor Gerring's avatar
Taylor Gerring committed
133
				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())
obscuren's avatar
obscuren committed
134 135
			}
		}
Taylor Gerring's avatar
Taylor Gerring committed
136
	}
137

Taylor Gerring's avatar
Taylor Gerring committed
138 139 140 141 142
	// check logs
	if len(test.Logs) > 0 {
		lerr := checkLogs(test.Logs, logs)
		if lerr != nil {
			return lerr
143
		}
obscuren's avatar
obscuren committed
144
	}
Taylor Gerring's avatar
Taylor Gerring committed
145

146
	return nil
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
}

func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, state.Logs, *big.Int, error) {
	var (
		to    = common.HexToAddress(exec["address"])
		from  = common.HexToAddress(exec["caller"])
		data  = common.FromHex(exec["data"])
		gas   = common.Big(exec["gas"])
		price = common.Big(exec["gasPrice"])
		value = common.Big(exec["value"])
	)
	// Reset the pre-compiled contracts for VM tests.
	vm.Precompiled = make(map[string]*vm.PrecompiledAccount)

	caller := state.GetOrNewStateObject(from)

	vmenv := NewEnvFromMap(state, env, exec)
	vmenv.vmTest = true
	vmenv.skipTransfer = true
	vmenv.initial = true
	ret, err := vmenv.Call(caller, to, data, gas, price, value)

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