123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // 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 (
- "encoding/hex"
- "encoding/json"
- "fmt"
- "math/big"
- "strings"
- "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/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/crypto/sha3"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- )
- // 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
- }
- // StateSubtest selects a specific configuration of a General State Test.
- type StateSubtest struct {
- Fork string
- Index int
- }
- func (t *StateTest) UnmarshalJSON(in []byte) error {
- return json.Unmarshal(in, &t.json)
- }
- 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"`
- Logs common.UnprefixedHash `json:"logs"`
- Indexes struct {
- Data int `json:"data"`
- Gas int `json:"gas"`
- Value int `json:"value"`
- }
- }
- //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
- type stEnv struct {
- Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
- Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
- GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
- Number uint64 `json:"currentNumber" gencodec:"required"`
- Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
- }
- type stEnvMarshaling struct {
- Coinbase common.UnprefixedAddress
- Difficulty *math.HexOrDecimal256
- GasLimit math.HexOrDecimal64
- 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 {
- for i := range pss {
- sub = append(sub, StateSubtest{fork, i})
- }
- }
- return sub
- }
- // Run executes a specific subtest.
- func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
- config, ok := Forks[subtest.Fork]
- if !ok {
- return nil, UnsupportedForkError{subtest.Fork}
- }
- block := t.genesis(config).ToBlock(nil)
- statedb := MakePreState(ethdb.NewMemDatabase(), t.json.Pre)
- post := t.json.Post[subtest.Fork][subtest.Index]
- msg, err := t.json.Tx.toMessage(post)
- if err != nil {
- return nil, err
- }
- context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
- context.GetHash = vmTestBlockHash
- evm := vm.NewEVM(context, statedb, config, vmconfig)
- gaspool := new(core.GasPool)
- gaspool.AddGas(block.GasLimit())
- snapshot := statedb.Snapshot()
- if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
- statedb.RevertToSnapshot(snapshot)
- }
- 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)
- }
- root, _ := statedb.Commit(config.IsEIP158(block.Number()))
- if root != common.Hash(post.Root) {
- return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
- }
- return statedb, nil
- }
- func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
- return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
- }
- func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
- 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.
- root, _ := statedb.Commit(false)
- statedb, _ = state.New(root, sdb)
- return statedb
- }
- func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
- return &core.Genesis{
- Config: config,
- Coinbase: t.json.Env.Coinbase,
- Difficulty: t.json.Env.Difficulty,
- GasLimit: t.json.Env.GasLimit,
- Number: t.json.Env.Number,
- Timestamp: t.json.Env.Timestamp,
- Alloc: t.json.Pre,
- }
- }
- 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)
- }
- 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)
- }
- }
- // 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)
- }
- msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true)
- return msg, nil
- }
- func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewKeccak256()
- rlp.Encode(hw, x)
- hw.Sum(h[:0])
- return h
- }
|