vm_test_util.go 6.32 KB
Newer Older
1
// Copyright 2014 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"
obscuren's avatar
obscuren committed
32 33
)

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

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

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

	return nil
}

52 53 54
type bconf struct {
	name    string
	precomp bool
55
	jit     bool
56 57 58 59 60 61 62 63 64 65 66 67 68 69
}

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

70 71
	pJit := vm.EnableJit
	vm.EnableJit = conf.jit
72 73 74 75 76 77 78 79 80 81 82 83 84 85
	pForceJit := vm.ForceJit
	vm.ForceJit = conf.precomp

	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
86

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	/*
		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)
	}

102
	vm.EnableJit = pJit
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
	vm.ForceJit = pForceJit

	return nil
}

func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
	b.StopTimer()
	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))
		}
	}
	b.StartTimer()

	RunVm(statedb, env, test.Exec)
}

func RunVmTest(p string, skipTests []string) error {
obscuren's avatar
obscuren committed
125
	tests := make(map[string]VmTest)
Taylor Gerring's avatar
Taylor Gerring committed
126
	err := readJsonFile(p, &tests)
127 128 129
	if err != nil {
		return err
	}
obscuren's avatar
obscuren committed
130

131
	if err := runVmTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
132 133 134 135 136 137
		return err
	}

	return nil
}

138 139 140
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
141 142 143
		skipTest[name] = true
	}

obscuren's avatar
obscuren committed
144
	for name, test := range tests {
145
		if skipTest[name] {
Taylor Gerring's avatar
Taylor Gerring committed
146
			glog.Infoln("Skipping VM test", name)
147 148
			return nil
		}
Taylor Gerring's avatar
Taylor Gerring committed
149 150 151

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

Taylor Gerring's avatar
Taylor Gerring committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167
		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
168
		}
Taylor Gerring's avatar
Taylor Gerring committed
169
	}
obscuren's avatar
obscuren committed
170

Taylor Gerring's avatar
Taylor Gerring committed
171 172 173 174 175 176 177 178 179 180 181 182
	// 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
183

Taylor Gerring's avatar
Taylor Gerring committed
184 185 186 187 188 189
	var (
		ret  []byte
		gas  *big.Int
		err  error
		logs state.Logs
	)
obscuren's avatar
obscuren committed
190

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

Taylor Gerring's avatar
Taylor Gerring committed
193
	// Compare expected and actual return
Taylor Gerring's avatar
Taylor Gerring committed
194 195 196 197 198 199 200 201 202 203 204 205
	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
206
		}
Taylor Gerring's avatar
Taylor Gerring committed
207
	}
obscuren's avatar
obscuren committed
208

Taylor Gerring's avatar
Taylor Gerring committed
209 210 211 212 213 214
	// check post state
	for addr, account := range test.Post {
		obj := statedb.GetStateObject(common.HexToAddress(addr))
		if obj == nil {
			continue
		}
obscuren's avatar
obscuren committed
215

Taylor Gerring's avatar
Taylor Gerring committed
216 217 218
		for addr, value := range account.Storage {
			v := obj.GetState(common.HexToHash(addr))
			vexp := common.HexToHash(value)
obscuren's avatar
obscuren committed
219

Taylor Gerring's avatar
Taylor Gerring committed
220
			if v != vexp {
Taylor Gerring's avatar
Taylor Gerring committed
221
				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
222 223
			}
		}
Taylor Gerring's avatar
Taylor Gerring committed
224
	}
225

Taylor Gerring's avatar
Taylor Gerring committed
226 227 228 229 230
	// check logs
	if len(test.Logs) > 0 {
		lerr := checkLogs(test.Logs, logs)
		if lerr != nil {
			return lerr
231
		}
obscuren's avatar
obscuren committed
232
	}
Taylor Gerring's avatar
Taylor Gerring committed
233

234
	return nil
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
}

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
}