vm_test_util.go 5.86 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 := makePreState(db, test.Pre)
105 106 107 108 109 110
	b.StartTimer()

	RunVm(statedb, env, test.Exec)
}

func RunVmTest(p string, skipTests []string) error {
obscuren's avatar
obscuren committed
111
	tests := make(map[string]VmTest)
Taylor Gerring's avatar
Taylor Gerring committed
112
	err := readJsonFile(p, &tests)
113 114 115
	if err != nil {
		return err
	}
obscuren's avatar
obscuren committed
116

117
	if err := runVmTests(tests, skipTests); err != nil {
Taylor Gerring's avatar
Taylor Gerring committed
118 119 120 121 122 123
		return err
	}

	return nil
}

124 125 126
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
127 128 129
		skipTest[name] = true
	}

obscuren's avatar
obscuren committed
130
	for name, test := range tests {
131
		if skipTest[name] /*|| name != "loop_stacklimit_1021"*/ {
Taylor Gerring's avatar
Taylor Gerring committed
132
			glog.Infoln("Skipping VM test", name)
133
			continue
134
		}
Taylor Gerring's avatar
Taylor Gerring committed
135 136 137

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

Taylor Gerring's avatar
Taylor Gerring committed
140 141 142 143 144 145 146 147
		glog.Infoln("VM test passed: ", name)
		//fmt.Println(string(statedb.Dump()))
	}
	return nil
}

func runVmTest(test VmTest) error {
	db, _ := ethdb.NewMemDatabase()
148
	statedb := makePreState(db, test.Pre)
obscuren's avatar
obscuren committed
149

Taylor Gerring's avatar
Taylor Gerring committed
150 151 152 153 154 155 156 157 158 159 160 161
	// 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
162

Taylor Gerring's avatar
Taylor Gerring committed
163 164 165 166
	var (
		ret  []byte
		gas  *big.Int
		err  error
167
		logs vm.Logs
Taylor Gerring's avatar
Taylor Gerring committed
168
	)
obscuren's avatar
obscuren committed
169

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

Taylor Gerring's avatar
Taylor Gerring committed
172
	// Compare expected and actual return
Taylor Gerring's avatar
Taylor Gerring committed
173 174 175 176 177 178 179 180 181 182 183 184
	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
185
		}
Taylor Gerring's avatar
Taylor Gerring committed
186
	}
obscuren's avatar
obscuren committed
187

Taylor Gerring's avatar
Taylor Gerring committed
188 189 190 191 192 193 194
	// check post state
	for addr, account := range test.Post {
		obj := statedb.GetStateObject(common.HexToAddress(addr))
		if obj == nil {
			continue
		}
		for addr, value := range account.Storage {
195
			v := statedb.GetState(obj.Address(), common.HexToHash(addr))
Taylor Gerring's avatar
Taylor Gerring committed
196 197
			vexp := common.HexToHash(value)
			if v != vexp {
Taylor Gerring's avatar
Taylor Gerring committed
198
				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
199 200
			}
		}
Taylor Gerring's avatar
Taylor Gerring committed
201
	}
202

Taylor Gerring's avatar
Taylor Gerring committed
203 204 205 206 207
	// check logs
	if len(test.Logs) > 0 {
		lerr := checkLogs(test.Logs, logs)
		if lerr != nil {
			return lerr
208
		}
obscuren's avatar
obscuren committed
209
	}
Taylor Gerring's avatar
Taylor Gerring committed
210

211
	return nil
212 213
}

214 215 216 217 218 219
func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
	chainConfig := &params.ChainConfig{
		HomesteadBlock: params.MainNetHomesteadBlock,
		DAOForkBlock:   params.MainNetDAOForkBlock,
		DAOForkSupport: true,
	}
220 221 222 223 224 225 226
	var (
		to    = common.HexToAddress(exec["address"])
		from  = common.HexToAddress(exec["caller"])
		data  = common.FromHex(exec["data"])
		gas   = common.Big(exec["gas"])
		value = common.Big(exec["value"])
	)
227
	caller := statedb.GetOrNewStateObject(from)
228
	vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
229

230 231 232
	environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
	ret, err := environment.Call(caller, to, data, gas, value)
	return ret, statedb.Logs(), gas, err
233
}