vm_test_util.go 6.25 KB
Newer Older
1
// Copyright 2015 The go-ethereum Authors
2
// This file is part of the go-ethereum library.
3
//
4
// The go-ethereum library is free software: you can redistribute it and/or modify
5 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
9
// The go-ethereum library is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 13 14
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16

17
package tests
obscuren's avatar
obscuren committed
18 19 20

import (
	"bytes"
21
	"fmt"
Taylor Gerring's avatar
Taylor Gerring committed
22
	"io"
obscuren's avatar
obscuren committed
23 24
	"math/big"
	"strconv"
25
	"testing"
obscuren's avatar
obscuren committed
26

obscuren's avatar
obscuren committed
27
	"github.com/ethereum/go-ethereum/common"
28
	"github.com/ethereum/go-ethereum/core/state"
29
	"github.com/ethereum/go-ethereum/core/vm"
obscuren's avatar
obscuren committed
30
	"github.com/ethereum/go-ethereum/ethdb"
Taylor Gerring's avatar
Taylor Gerring committed
31
	"github.com/ethereum/go-ethereum/logger/glog"
32
	"github.com/ethereum/go-ethereum/params"
obscuren's avatar
obscuren committed
33 34
)

35
func RunVmTestWithReader(r io.Reader, skipTests []string) error {
Taylor Gerring's avatar
Taylor Gerring committed
36 37 38 39
	tests := make(map[string]VmTest)
	err := readJson(r, &tests)
	if err != nil {
		return err
40
	}
41

Taylor Gerring's avatar
Taylor Gerring committed
42 43 44 45
	if err != nil {
		return err
	}

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

	return nil
}

53 54 55
type bconf struct {
	name    string
	precomp bool
56
	jit     bool
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
}

func BenchVmTest(p string, conf bconf, b *testing.B) error {
	tests := make(map[string]VmTest)
	err := readJsonFile(p, &tests)
	if err != nil {
		return err
	}

	test, ok := tests[conf.name]
	if !ok {
		return fmt.Errorf("test not found: %s", conf.name)
	}

	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)
	}
Taylor Gerring's avatar
Taylor Gerring committed
82

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	/*
		if conf.precomp {
			program := vm.NewProgram(test.code)
			err := vm.AttachProgram(program)
			if err != nil {
				return err
			}
		}
	*/

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		benchVmTest(test, env, b)
	}

	return nil
}

func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
	b.StopTimer()
	db, _ := ethdb.NewMemDatabase()
104
	statedb, _ := state.New(common.Hash{}, db)
105 106 107 108 109 110 111 112 113 114 115 116 117
	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))
		}
	}
	b.StartTimer()

	RunVm(statedb, env, test.Exec)
}

func RunVmTest(p string, skipTests []string) error {
obscuren's avatar
obscuren committed
118
	tests := make(map[string]VmTest)
Taylor Gerring's avatar
Taylor Gerring committed
119
	err := readJsonFile(p, &tests)
120 121 122
	if err != nil {
		return err
	}
obscuren's avatar
obscuren committed
123

124
	if err := runVmTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
125 126 127 128 129 130
		return err
	}

	return nil
}

131 132 133
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
134 135 136
		skipTest[name] = true
	}

obscuren's avatar
obscuren committed
137
	for name, test := range tests {
138
		if skipTest[name] {
Taylor Gerring's avatar
Taylor Gerring committed
139
			glog.Infoln("Skipping VM test", name)
140 141
			return nil
		}
Taylor Gerring's avatar
Taylor Gerring committed
142 143 144

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

Taylor Gerring's avatar
Taylor Gerring committed
147 148 149 150 151 152 153 154
		glog.Infoln("VM test passed: ", name)
		//fmt.Println(string(statedb.Dump()))
	}
	return nil
}

func runVmTest(test VmTest) error {
	db, _ := ethdb.NewMemDatabase()
155
	statedb, _ := state.New(common.Hash{}, db)
Taylor Gerring's avatar
Taylor Gerring committed
156 157 158 159 160
	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
161
		}
Taylor Gerring's avatar
Taylor Gerring committed
162
	}
obscuren's avatar
obscuren committed
163

Taylor Gerring's avatar
Taylor Gerring committed
164 165 166 167 168 169 170 171 172 173 174 175
	// 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
176

Taylor Gerring's avatar
Taylor Gerring committed
177 178 179 180
	var (
		ret  []byte
		gas  *big.Int
		err  error
181
		logs vm.Logs
Taylor Gerring's avatar
Taylor Gerring committed
182
	)
obscuren's avatar
obscuren committed
183

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

Taylor Gerring's avatar
Taylor Gerring committed
186
	// Compare expected and actual return
Taylor Gerring's avatar
Taylor Gerring committed
187 188 189 190 191 192 193 194 195 196 197 198
	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
199
		}
Taylor Gerring's avatar
Taylor Gerring committed
200
	}
obscuren's avatar
obscuren committed
201

Taylor Gerring's avatar
Taylor Gerring committed
202 203 204 205 206 207
	// check post state
	for addr, account := range test.Post {
		obj := statedb.GetStateObject(common.HexToAddress(addr))
		if obj == nil {
			continue
		}
obscuren's avatar
obscuren committed
208

Taylor Gerring's avatar
Taylor Gerring committed
209 210 211
		for addr, value := range account.Storage {
			v := obj.GetState(common.HexToHash(addr))
			vexp := common.HexToHash(value)
obscuren's avatar
obscuren committed
212

Taylor Gerring's avatar
Taylor Gerring committed
213
			if v != vexp {
Taylor Gerring's avatar
Taylor Gerring committed
214
				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
215 216
			}
		}
Taylor Gerring's avatar
Taylor Gerring committed
217
	}
218

Taylor Gerring's avatar
Taylor Gerring committed
219 220 221 222 223
	// check logs
	if len(test.Logs) > 0 {
		lerr := checkLogs(test.Logs, logs)
		if lerr != nil {
			return lerr
224
		}
obscuren's avatar
obscuren committed
225
	}
Taylor Gerring's avatar
Taylor Gerring committed
226

227
	return nil
228 229
}

230
func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
231 232 233 234 235 236 237 238 239 240 241 242 243
	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)

244
	vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock}, state, env, exec)
245 246 247 248 249 250 251
	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
}