state_test_util.go 8.15 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 18 19
package tests

import (
20 21
	"encoding/hex"
	"encoding/json"
22
	"fmt"
23
	"math/big"
24
	"strings"
25 26

	"github.com/ethereum/go-ethereum/common"
27
	"github.com/ethereum/go-ethereum/common/hexutil"
28
	"github.com/ethereum/go-ethereum/common/math"
29
	"github.com/ethereum/go-ethereum/core"
30
	"github.com/ethereum/go-ethereum/core/rawdb"
31
	"github.com/ethereum/go-ethereum/core/state"
32
	"github.com/ethereum/go-ethereum/core/types"
33 34
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/crypto"
35
	"github.com/ethereum/go-ethereum/ethdb"
36
	"github.com/ethereum/go-ethereum/params"
37
	"github.com/ethereum/go-ethereum/rlp"
38
	"golang.org/x/crypto/sha3"
39 40
)

41 42 43 44 45
// StateTest checks transaction processing without block context.
// See https://github.com/ethereum/EIPs/issues/176 for the test format specification.
type StateTest struct {
	json stJSON
}
Taylor Gerring's avatar
Taylor Gerring committed
46

47 48 49 50
// StateSubtest selects a specific configuration of a General State Test.
type StateSubtest struct {
	Fork  string
	Index int
Taylor Gerring's avatar
Taylor Gerring committed
51 52
}

53 54 55
func (t *StateTest) UnmarshalJSON(in []byte) error {
	return json.Unmarshal(in, &t.json)
}
Taylor Gerring's avatar
Taylor Gerring committed
56

57 58 59 60 61 62 63 64 65 66
type stJSON struct {
	Env  stEnv                    `json:"env"`
	Pre  core.GenesisAlloc        `json:"pre"`
	Tx   stTransaction            `json:"transaction"`
	Out  hexutil.Bytes            `json:"out"`
	Post map[string][]stPostState `json:"post"`
}

type stPostState struct {
	Root    common.UnprefixedHash `json:"hash"`
67
	Logs    common.UnprefixedHash `json:"logs"`
68 69 70 71
	Indexes struct {
		Data  int `json:"data"`
		Gas   int `json:"gas"`
		Value int `json:"value"`
Taylor Gerring's avatar
Taylor Gerring committed
72
	}
73
}
Taylor Gerring's avatar
Taylor Gerring committed
74

75
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
Taylor Gerring's avatar
Taylor Gerring committed
76

77 78 79
type stEnv struct {
	Coinbase   common.Address `json:"currentCoinbase"   gencodec:"required"`
	Difficulty *big.Int       `json:"currentDifficulty" gencodec:"required"`
80
	GasLimit   uint64         `json:"currentGasLimit"   gencodec:"required"`
81 82
	Number     uint64         `json:"currentNumber"     gencodec:"required"`
	Timestamp  uint64         `json:"currentTimestamp"  gencodec:"required"`
Taylor Gerring's avatar
Taylor Gerring committed
83 84
}

85 86 87
type stEnvMarshaling struct {
	Coinbase   common.UnprefixedAddress
	Difficulty *math.HexOrDecimal256
88
	GasLimit   math.HexOrDecimal64
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	Number     math.HexOrDecimal64
	Timestamp  math.HexOrDecimal64
}

//go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go

type stTransaction struct {
	GasPrice   *big.Int `json:"gasPrice"`
	Nonce      uint64   `json:"nonce"`
	To         string   `json:"to"`
	Data       []string `json:"data"`
	GasLimit   []uint64 `json:"gasLimit"`
	Value      []string `json:"value"`
	PrivateKey []byte   `json:"secretKey"`
}

type stTransactionMarshaling struct {
	GasPrice   *math.HexOrDecimal256
	Nonce      math.HexOrDecimal64
	GasLimit   []math.HexOrDecimal64
	PrivateKey hexutil.Bytes
}

// Subtests returns all valid subtests of the test.
func (t *StateTest) Subtests() []StateSubtest {
	var sub []StateSubtest
	for fork, pss := range t.json.Post {
116
		for i := range pss {
117 118
			sub = append(sub, StateSubtest{fork, i})
		}
119
	}
120 121 122 123
	return sub
}

// Run executes a specific subtest.
124
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
125
	config, ok := Forks[subtest.Fork]
126
	if !ok {
127
		return nil, UnsupportedForkError{subtest.Fork}
128
	}
129
	block := t.genesis(config).ToBlock(nil)
130
	statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre)
131

132 133 134
	post := t.json.Post[subtest.Fork][subtest.Index]
	msg, err := t.json.Tx.toMessage(post)
	if err != nil {
135
		return nil, err
136
	}
137 138 139
	context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
	context.GetHash = vmTestBlockHash
	evm := vm.NewEVM(context, statedb, config, vmconfig)
140

141 142 143
	gaspool := new(core.GasPool)
	gaspool.AddGas(block.GasLimit())
	snapshot := statedb.Snapshot()
144
	if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
145 146
		statedb.RevertToSnapshot(snapshot)
	}
147 148 149 150 151 152 153 154 155 156 157 158
	// Commit block
	statedb.Commit(config.IsEIP158(block.Number()))
	// Add 0-value mining reward. This only makes a difference in the cases
	// where
	// - the coinbase suicided, or
	// - there are only 'bad' transactions, which aren't executed. In those cases,
	//   the coinbase gets no txfee, so isn't created, and thus needs to be touched
	statedb.AddBalance(block.Coinbase(), new(big.Int))
	// And _now_ get the state root
	root := statedb.IntermediateRoot(config.IsEIP158(block.Number()))
	// N.B: We need to do this in a two-step process, because the first Commit takes care
	// of suicides, and we need to touch the coinbase _after_ it has potentially suicided.
159
	if root != common.Hash(post.Root) {
160
		return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
161
	}
162 163 164
	if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
		return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
	}
165
	return statedb, nil
166 167
}

168 169 170
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
	return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
}
171

172
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
173 174 175 176 177 178 179 180 181 182 183
	sdb := state.NewDatabase(db)
	statedb, _ := state.New(common.Hash{}, sdb)
	for addr, a := range accounts {
		statedb.SetCode(addr, a.Code)
		statedb.SetNonce(addr, a.Nonce)
		statedb.SetBalance(addr, a.Balance)
		for k, v := range a.Storage {
			statedb.SetState(addr, k, v)
		}
	}
	// Commit and re-open to start with a clean state.
184
	root, _ := statedb.Commit(false)
185 186
	statedb, _ = state.New(root, sdb)
	return statedb
187 188
}

189 190 191 192 193
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
	return &core.Genesis{
		Config:     config,
		Coinbase:   t.json.Env.Coinbase,
		Difficulty: t.json.Env.Difficulty,
194
		GasLimit:   t.json.Env.GasLimit,
195 196 197
		Number:     t.json.Env.Number,
		Timestamp:  t.json.Env.Timestamp,
		Alloc:      t.json.Pre,
Taylor Gerring's avatar
Taylor Gerring committed
198
	}
199
}
200

201 202 203 204 205 206 207
func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) {
	// Derive sender from private key if present.
	var from common.Address
	if len(tx.PrivateKey) > 0 {
		key, err := crypto.ToECDSA(tx.PrivateKey)
		if err != nil {
			return nil, fmt.Errorf("invalid private key: %v", err)
208
		}
209 210 211 212 213 214 215 216
		from = crypto.PubkeyToAddress(key.PublicKey)
	}
	// Parse recipient if present.
	var to *common.Address
	if tx.To != "" {
		to = new(common.Address)
		if err := to.UnmarshalText([]byte(tx.To)); err != nil {
			return nil, fmt.Errorf("invalid to address: %v", err)
217
		}
218
	}
219

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
	// Get values specific to this post state.
	if ps.Indexes.Data > len(tx.Data) {
		return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data)
	}
	if ps.Indexes.Value > len(tx.Value) {
		return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value)
	}
	if ps.Indexes.Gas > len(tx.GasLimit) {
		return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas)
	}
	dataHex := tx.Data[ps.Indexes.Data]
	valueHex := tx.Value[ps.Indexes.Value]
	gasLimit := tx.GasLimit[ps.Indexes.Gas]
	// Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203
	value := new(big.Int)
	if valueHex != "0x" {
		v, ok := math.ParseBig256(valueHex)
		if !ok {
			return nil, fmt.Errorf("invalid tx value %q", valueHex)
		}
		value = v
	}
	data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x"))
	if err != nil {
		return nil, fmt.Errorf("invalid tx data %q", dataHex)
Taylor Gerring's avatar
Taylor Gerring committed
245
	}
246

247
	msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true)
248
	return msg, nil
Taylor Gerring's avatar
Taylor Gerring committed
249
}
250

251
func rlpHash(x interface{}) (h common.Hash) {
252
	hw := sha3.NewLegacyKeccak256()
253 254 255
	rlp.Encode(hw, x)
	hw.Sum(h[:0])
	return h
256
}