// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // 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. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package tests import ( "bytes" "encoding/json" "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) // VMTest checks EVM execution without block or transaction context. // See https://github.com/ethereum/tests/wiki/VM-Tests for the test format specification. type VMTest struct { json vmJSON } func (t *VMTest) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &t.json) } type vmJSON struct { Env stEnv `json:"env"` Exec vmExec `json:"exec"` Logs common.UnprefixedHash `json:"logs"` GasRemaining *math.HexOrDecimal64 `json:"gas"` Out hexutil.Bytes `json:"out"` Pre core.GenesisAlloc `json:"pre"` Post core.GenesisAlloc `json:"post"` PostStateRoot common.Hash `json:"postStateRoot"` } //go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go type vmExec struct { Address common.Address `json:"address" gencodec:"required"` Caller common.Address `json:"caller" gencodec:"required"` Origin common.Address `json:"origin" gencodec:"required"` Code []byte `json:"code" gencodec:"required"` Data []byte `json:"data" gencodec:"required"` Value *big.Int `json:"value" gencodec:"required"` GasLimit uint64 `json:"gas" gencodec:"required"` GasPrice *big.Int `json:"gasPrice" gencodec:"required"` } type vmExecMarshaling struct { Address common.UnprefixedAddress Caller common.UnprefixedAddress Origin common.UnprefixedAddress Code hexutil.Bytes Data hexutil.Bytes Value *math.HexOrDecimal256 GasLimit math.HexOrDecimal64 GasPrice *math.HexOrDecimal256 } func (t *VMTest) Run(vmconfig vm.Config) error { statedb := MakePreState(ethdb.NewMemDatabase(), t.json.Pre) ret, gasRemaining, err := t.exec(statedb, vmconfig) if t.json.GasRemaining == nil { if err == nil { return fmt.Errorf("gas unspecified (indicating an error), but VM returned no error") } if gasRemaining > 0 { return fmt.Errorf("gas unspecified (indicating an error), but VM returned gas remaining > 0") } return nil } // Test declares gas, expecting outputs to match. if !bytes.Equal(ret, t.json.Out) { return fmt.Errorf("return data mismatch: got %x, want %x", ret, t.json.Out) } if gasRemaining != uint64(*t.json.GasRemaining) { return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining) } for addr, account := range t.json.Post { for k, wantV := range account.Storage { if haveV := statedb.GetState(addr, k); haveV != wantV { return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV) } } } // if root := statedb.IntermediateRoot(false); root != t.json.PostStateRoot { // return fmt.Errorf("post state root mismatch, got %x, want %x", root, t.json.PostStateRoot) // } if logs := rlpHash(statedb.Logs()); logs != common.Hash(t.json.Logs) { return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, t.json.Logs) } return nil } func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) { evm := t.newEVM(statedb, vmconfig) e := t.json.Exec return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value) } func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { initialCall := true canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool { if initialCall { initialCall = false return true } return core.CanTransfer(db, address, amount) } transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {} context := vm.Context{ CanTransfer: canTransfer, Transfer: transfer, GetHash: vmTestBlockHash, Origin: t.json.Exec.Origin, Coinbase: t.json.Env.Coinbase, BlockNumber: new(big.Int).SetUint64(t.json.Env.Number), Time: new(big.Int).SetUint64(t.json.Env.Timestamp), GasLimit: t.json.Env.GasLimit, Difficulty: t.json.Env.Difficulty, GasPrice: t.json.Exec.GasPrice, } vmconfig.NoRecursion = true return vm.NewEVM(context, statedb, params.MainnetChainConfig, vmconfig) } func vmTestBlockHash(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) }