160 lines
5.6 KiB
Go
160 lines
5.6 KiB
Go
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
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/rawdb"
|
|
"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/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, snapshotter bool) error {
|
|
snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter)
|
|
if snapshotter {
|
|
preRoot := statedb.IntermediateRoot(false)
|
|
defer func() {
|
|
if _, err := snaps.Journal(preRoot); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
}
|
|
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())))
|
|
}
|