github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/tests/state_test_util.go (about)

     1  package tests
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  
    10  	"github.com/neatlab/neatio/chain/core"
    11  	"github.com/neatlab/neatio/chain/core/rawdb"
    12  	"github.com/neatlab/neatio/chain/core/state"
    13  	"github.com/neatlab/neatio/chain/core/types"
    14  	"github.com/neatlab/neatio/chain/core/vm"
    15  	"github.com/neatlab/neatio/neatdb"
    16  	"github.com/neatlab/neatio/params"
    17  	"github.com/neatlab/neatio/utilities/common"
    18  	"github.com/neatlab/neatio/utilities/common/hexutil"
    19  	"github.com/neatlab/neatio/utilities/common/math"
    20  	"github.com/neatlab/neatio/utilities/crypto"
    21  	"github.com/neatlab/neatio/utilities/rlp"
    22  	"golang.org/x/crypto/sha3"
    23  )
    24  
    25  type StateTest struct {
    26  	json stJSON
    27  }
    28  
    29  // StateSubtest selects a specific configuration of a General State Test.
    30  type StateSubtest struct {
    31  	Fork  string
    32  	Index int
    33  }
    34  
    35  func (t *StateTest) UnmarshalJSON(in []byte) error {
    36  	return json.Unmarshal(in, &t.json)
    37  }
    38  
    39  type stJSON struct {
    40  	Env  stEnv                    `json:"env"`
    41  	Pre  core.GenesisAlloc        `json:"pre"`
    42  	Tx   stTransaction            `json:"transaction"`
    43  	Out  hexutil.Bytes            `json:"out"`
    44  	Post map[string][]stPostState `json:"post"`
    45  }
    46  
    47  type stPostState struct {
    48  	Root    common.UnprefixedHash `json:"hash"`
    49  	Logs    common.UnprefixedHash `json:"logs"`
    50  	Indexes struct {
    51  		Data  int `json:"data"`
    52  		Gas   int `json:"gas"`
    53  		Value int `json:"value"`
    54  	}
    55  }
    56  
    57  //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
    58  
    59  type stEnv struct {
    60  	Coinbase   common.Address `json:"currentCoinbase"   gencodec:"required"`
    61  	Difficulty *big.Int       `json:"currentDifficulty" gencodec:"required"`
    62  	GasLimit   uint64         `json:"currentGasLimit"   gencodec:"required"`
    63  	Number     uint64         `json:"currentNumber"     gencodec:"required"`
    64  	Timestamp  uint64         `json:"currentTimestamp"  gencodec:"required"`
    65  }
    66  
    67  type stEnvMarshaling struct {
    68  	Coinbase   common.UnprefixedAddress
    69  	Difficulty *math.HexOrDecimal256
    70  	GasLimit   math.HexOrDecimal64
    71  	Number     math.HexOrDecimal64
    72  	Timestamp  math.HexOrDecimal64
    73  }
    74  
    75  //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
    76  
    77  type stTransaction struct {
    78  	GasPrice   *big.Int `json:"gasPrice"`
    79  	Nonce      uint64   `json:"nonce"`
    80  	To         string   `json:"to"`
    81  	Data       []string `json:"data"`
    82  	GasLimit   []uint64 `json:"gasLimit"`
    83  	Value      []string `json:"value"`
    84  	PrivateKey []byte   `json:"secretKey"`
    85  }
    86  
    87  type stTransactionMarshaling struct {
    88  	GasPrice   *math.HexOrDecimal256
    89  	Nonce      math.HexOrDecimal64
    90  	GasLimit   []math.HexOrDecimal64
    91  	PrivateKey hexutil.Bytes
    92  }
    93  
    94  // Subtests returns all valid subtests of the test.
    95  func (t *StateTest) Subtests() []StateSubtest {
    96  	var sub []StateSubtest
    97  	for fork, pss := range t.json.Post {
    98  		for i := range pss {
    99  			sub = append(sub, StateSubtest{fork, i})
   100  		}
   101  	}
   102  	return sub
   103  }
   104  
   105  // Run executes a specific subtest.
   106  func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
   107  	config, ok := Forks[subtest.Fork]
   108  	if !ok {
   109  		return nil, UnsupportedForkError{subtest.Fork}
   110  	}
   111  	block := t.genesis(config).ToBlock(nil)
   112  	db := rawdb.NewMemoryDatabase()
   113  	statedb := MakePreState(db, t.json.Pre)
   114  
   115  	post := t.json.Post[subtest.Fork][subtest.Index]
   116  	msg, err := t.json.Tx.toMessage(post)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
   121  	context.GetHash = vmTestBlockHash
   122  	evm := vm.NewEVM(context, statedb, config, vmconfig)
   123  
   124  	gaspool := new(core.GasPool)
   125  	gaspool.AddGas(block.GasLimit())
   126  	snapshot := statedb.Snapshot()
   127  	if _, _, err := core.ApplyMessageEx(evm, msg, gaspool); err != nil {
   128  		statedb.RevertToSnapshot(snapshot)
   129  	}
   130  	if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
   131  		return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
   132  	}
   133  	root, _ := statedb.Commit(config.IsEIP158(block.Number()))
   134  	if root != common.Hash(post.Root) {
   135  		return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
   136  	}
   137  	return statedb, nil
   138  }
   139  
   140  func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
   141  	return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
   142  }
   143  
   144  func MakePreState(db neatdb.Database, accounts core.GenesisAlloc) *state.StateDB {
   145  	sdb := state.NewDatabase(db)
   146  	statedb, _ := state.New(common.Hash{}, sdb)
   147  	for addr, a := range accounts {
   148  		statedb.SetCode(addr, a.Code)
   149  		statedb.SetNonce(addr, a.Nonce)
   150  		statedb.SetBalance(addr, a.Balance)
   151  		for k, v := range a.Storage {
   152  			statedb.SetState(addr, k, v)
   153  		}
   154  	}
   155  	// Commit and re-open to start with a clean state.
   156  	root, _ := statedb.Commit(false)
   157  	statedb, _ = state.New(root, sdb)
   158  	return statedb
   159  }
   160  
   161  func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
   162  	return &core.Genesis{
   163  		Config:     config,
   164  		Coinbase:   t.json.Env.Coinbase,
   165  		Difficulty: t.json.Env.Difficulty,
   166  		GasLimit:   t.json.Env.GasLimit,
   167  		Number:     t.json.Env.Number,
   168  		Timestamp:  t.json.Env.Timestamp,
   169  		Alloc:      t.json.Pre,
   170  	}
   171  }
   172  
   173  func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) {
   174  	// Derive sender from private key if present.
   175  	var from common.Address
   176  	if len(tx.PrivateKey) > 0 {
   177  		key, err := crypto.ToECDSA(tx.PrivateKey)
   178  		if err != nil {
   179  			return nil, fmt.Errorf("invalid private key: %v", err)
   180  		}
   181  		from = crypto.PubkeyToAddress(key.PublicKey)
   182  	}
   183  	// Parse recipient if present.
   184  	var to *common.Address
   185  	if tx.To != "" {
   186  		to = new(common.Address)
   187  		if err := to.UnmarshalText([]byte(tx.To)); err != nil {
   188  			return nil, fmt.Errorf("invalid to address: %v", err)
   189  		}
   190  	}
   191  
   192  	// Get values specific to this post state.
   193  	if ps.Indexes.Data > len(tx.Data) {
   194  		return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data)
   195  	}
   196  	if ps.Indexes.Value > len(tx.Value) {
   197  		return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value)
   198  	}
   199  	if ps.Indexes.Gas > len(tx.GasLimit) {
   200  		return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas)
   201  	}
   202  	dataHex := tx.Data[ps.Indexes.Data]
   203  	valueHex := tx.Value[ps.Indexes.Value]
   204  	gasLimit := tx.GasLimit[ps.Indexes.Gas]
   205  	// Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203
   206  	value := new(big.Int)
   207  	if valueHex != "0x" {
   208  		v, ok := math.ParseBig256(valueHex)
   209  		if !ok {
   210  			return nil, fmt.Errorf("invalid tx value %q", valueHex)
   211  		}
   212  		value = v
   213  	}
   214  	data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x"))
   215  	if err != nil {
   216  		return nil, fmt.Errorf("invalid tx data %q", dataHex)
   217  	}
   218  
   219  	msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true)
   220  	return msg, nil
   221  }
   222  
   223  func rlpHash(x interface{}) (h common.Hash) {
   224  	hw := sha3.NewLegacyKeccak256()
   225  	rlp.Encode(hw, x)
   226  	hw.Sum(h[:0])
   227  	return h
   228  }