github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/tests/vm_test_util.go (about)

     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  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     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  // GNU Lesser General Public License for more details.
    13  //
    14  // 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
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"math/big"
    24  	"strconv"
    25  	"testing"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core/state"
    29  	"github.com/ethereum/go-ethereum/core/vm"
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/logger/glog"
    32  )
    33  
    34  func RunVmTestWithReader(r io.Reader, skipTests []string) error {
    35  	tests := make(map[string]VmTest)
    36  	err := readJson(r, &tests)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	if err := runVmTests(tests, skipTests); err != nil {
    46  		return err
    47  	}
    48  
    49  	return nil
    50  }
    51  
    52  type bconf struct {
    53  	name    string
    54  	precomp bool
    55  	jit     bool
    56  }
    57  
    58  func BenchVmTest(p string, conf bconf, b *testing.B) error {
    59  	tests := make(map[string]VmTest)
    60  	err := readJsonFile(p, &tests)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	test, ok := tests[conf.name]
    66  	if !ok {
    67  		return fmt.Errorf("test not found: %s", conf.name)
    68  	}
    69  
    70  	pJit := vm.EnableJit
    71  	vm.EnableJit = conf.jit
    72  	pForceJit := vm.ForceJit
    73  	vm.ForceJit = conf.precomp
    74  
    75  	env := make(map[string]string)
    76  	env["currentCoinbase"] = test.Env.CurrentCoinbase
    77  	env["currentDifficulty"] = test.Env.CurrentDifficulty
    78  	env["currentGasLimit"] = test.Env.CurrentGasLimit
    79  	env["currentNumber"] = test.Env.CurrentNumber
    80  	env["previousHash"] = test.Env.PreviousHash
    81  	if n, ok := test.Env.CurrentTimestamp.(float64); ok {
    82  		env["currentTimestamp"] = strconv.Itoa(int(n))
    83  	} else {
    84  		env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
    85  	}
    86  
    87  	/*
    88  		if conf.precomp {
    89  			program := vm.NewProgram(test.code)
    90  			err := vm.AttachProgram(program)
    91  			if err != nil {
    92  				return err
    93  			}
    94  		}
    95  	*/
    96  
    97  	b.ResetTimer()
    98  	for i := 0; i < b.N; i++ {
    99  		benchVmTest(test, env, b)
   100  	}
   101  
   102  	vm.EnableJit = pJit
   103  	vm.ForceJit = pForceJit
   104  
   105  	return nil
   106  }
   107  
   108  func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
   109  	b.StopTimer()
   110  	db, _ := ethdb.NewMemDatabase()
   111  	statedb := state.New(common.Hash{}, db)
   112  	for addr, account := range test.Pre {
   113  		obj := StateObjectFromAccount(db, addr, account)
   114  		statedb.SetStateObject(obj)
   115  		for a, v := range account.Storage {
   116  			obj.SetState(common.HexToHash(a), common.HexToHash(v))
   117  		}
   118  	}
   119  	b.StartTimer()
   120  
   121  	RunVm(statedb, env, test.Exec)
   122  }
   123  
   124  func RunVmTest(p string, skipTests []string) error {
   125  	tests := make(map[string]VmTest)
   126  	err := readJsonFile(p, &tests)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	if err := runVmTests(tests, skipTests); err != nil {
   132  		return err
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func runVmTests(tests map[string]VmTest, skipTests []string) error {
   139  	skipTest := make(map[string]bool, len(skipTests))
   140  	for _, name := range skipTests {
   141  		skipTest[name] = true
   142  	}
   143  
   144  	for name, test := range tests {
   145  		if skipTest[name] {
   146  			glog.Infoln("Skipping VM test", name)
   147  			return nil
   148  		}
   149  
   150  		if err := runVmTest(test); err != nil {
   151  			return fmt.Errorf("%s %s", name, err.Error())
   152  		}
   153  
   154  		glog.Infoln("VM test passed: ", name)
   155  		//fmt.Println(string(statedb.Dump()))
   156  	}
   157  	return nil
   158  }
   159  
   160  func runVmTest(test VmTest) error {
   161  	db, _ := ethdb.NewMemDatabase()
   162  	statedb := state.New(common.Hash{}, db)
   163  	for addr, account := range test.Pre {
   164  		obj := StateObjectFromAccount(db, addr, account)
   165  		statedb.SetStateObject(obj)
   166  		for a, v := range account.Storage {
   167  			obj.SetState(common.HexToHash(a), common.HexToHash(v))
   168  		}
   169  	}
   170  
   171  	// XXX Yeah, yeah...
   172  	env := make(map[string]string)
   173  	env["currentCoinbase"] = test.Env.CurrentCoinbase
   174  	env["currentDifficulty"] = test.Env.CurrentDifficulty
   175  	env["currentGasLimit"] = test.Env.CurrentGasLimit
   176  	env["currentNumber"] = test.Env.CurrentNumber
   177  	env["previousHash"] = test.Env.PreviousHash
   178  	if n, ok := test.Env.CurrentTimestamp.(float64); ok {
   179  		env["currentTimestamp"] = strconv.Itoa(int(n))
   180  	} else {
   181  		env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
   182  	}
   183  
   184  	var (
   185  		ret  []byte
   186  		gas  *big.Int
   187  		err  error
   188  		logs state.Logs
   189  	)
   190  
   191  	ret, logs, gas, err = RunVm(statedb, env, test.Exec)
   192  
   193  	// Compare expected and actual return
   194  	rexp := common.FromHex(test.Out)
   195  	if bytes.Compare(rexp, ret) != 0 {
   196  		return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
   197  	}
   198  
   199  	// Check gas usage
   200  	if len(test.Gas) == 0 && err == nil {
   201  		return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successfull")
   202  	} else {
   203  		gexp := common.Big(test.Gas)
   204  		if gexp.Cmp(gas) != 0 {
   205  			return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas)
   206  		}
   207  	}
   208  
   209  	// check post state
   210  	for addr, account := range test.Post {
   211  		obj := statedb.GetStateObject(common.HexToAddress(addr))
   212  		if obj == nil {
   213  			continue
   214  		}
   215  
   216  		for addr, value := range account.Storage {
   217  			v := obj.GetState(common.HexToHash(addr))
   218  			vexp := common.HexToHash(value)
   219  
   220  			if v != vexp {
   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())
   222  			}
   223  		}
   224  	}
   225  
   226  	// check logs
   227  	if len(test.Logs) > 0 {
   228  		lerr := checkLogs(test.Logs, logs)
   229  		if lerr != nil {
   230  			return lerr
   231  		}
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, state.Logs, *big.Int, error) {
   238  	var (
   239  		to    = common.HexToAddress(exec["address"])
   240  		from  = common.HexToAddress(exec["caller"])
   241  		data  = common.FromHex(exec["data"])
   242  		gas   = common.Big(exec["gas"])
   243  		price = common.Big(exec["gasPrice"])
   244  		value = common.Big(exec["value"])
   245  	)
   246  	// Reset the pre-compiled contracts for VM tests.
   247  	vm.Precompiled = make(map[string]*vm.PrecompiledAccount)
   248  
   249  	caller := state.GetOrNewStateObject(from)
   250  
   251  	vmenv := NewEnvFromMap(state, env, exec)
   252  	vmenv.vmTest = true
   253  	vmenv.skipTransfer = true
   254  	vmenv.initial = true
   255  	ret, err := vmenv.Call(caller, to, data, gas, price, value)
   256  
   257  	return ret, vmenv.state.Logs(), vmenv.Gas, err
   258  }