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 }