aboutsummaryrefslogblamecommitdiffstats
path: root/tests/vm_test_util.go
blob: 0aa37955ce35fcea99c9bc7d45a037e58a7e8314 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                         
                                                
  
                                                                                  



                                                                              
                                                                             
                                                                 
                                                               


                                                                           
                                                                                  
 
             


               
                       
             
                  
 
                                                
                                                        
                                                     
                                              
                                                    
                                                 
                                                
                                               
                                                

 



                                                                                         

 

                                                   

 
                    







                                                                  

 










                                                                                       

 








                                         

 



                                                           
 


                                                                                                            
                 

                                                                                                                     
                 
                          
         


                                                                                           
         

                                                                                                  
         



                                                                                                                      

                         
         


                                                                                                          



                                                                                                      
 
 



                                                                                           

 
























                                                                                             
 

                                                                                          
 
// 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/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 {
    db, _ := ethdb.NewMemDatabase()
    statedb := makePreState(db, 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())))
}