github.com/klaytn/klaytn@v1.10.2/tests/state_test_util.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from tests/state_test_util.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package tests 22 23 import ( 24 "encoding/hex" 25 "encoding/json" 26 "fmt" 27 "math/big" 28 "strconv" 29 "strings" 30 31 "github.com/klaytn/klaytn/blockchain" 32 "github.com/klaytn/klaytn/blockchain/state" 33 "github.com/klaytn/klaytn/blockchain/types" 34 "github.com/klaytn/klaytn/blockchain/vm" 35 "github.com/klaytn/klaytn/common" 36 "github.com/klaytn/klaytn/common/hexutil" 37 "github.com/klaytn/klaytn/common/math" 38 "github.com/klaytn/klaytn/crypto" 39 "github.com/klaytn/klaytn/crypto/sha3" 40 "github.com/klaytn/klaytn/params" 41 "github.com/klaytn/klaytn/rlp" 42 "github.com/klaytn/klaytn/storage/database" 43 ) 44 45 // StateTest checks transaction processing without block context. 46 // See https://github.com/ethereum/EIPs/issues/176 for the test format specification. 47 type StateTest struct { 48 json stJSON 49 } 50 51 // StateSubtest selects a specific configuration of a General State Test. 52 type StateSubtest struct { 53 Fork string 54 Index int 55 } 56 57 func (t *StateTest) UnmarshalJSON(in []byte) error { 58 return json.Unmarshal(in, &t.json) 59 } 60 61 type stJSON struct { 62 Env stEnv `json:"env"` 63 Pre blockchain.GenesisAlloc `json:"pre"` 64 Tx stTransaction `json:"transaction"` 65 Out hexutil.Bytes `json:"out"` 66 Post map[string][]stPostState `json:"post"` 67 } 68 69 type stPostState struct { 70 Root common.UnprefixedHash `json:"hash"` 71 Logs common.UnprefixedHash `json:"logs"` 72 Indexes struct { 73 Data int `json:"data"` 74 Gas int `json:"gas"` 75 Value int `json:"value"` 76 } 77 } 78 79 //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go 80 81 type stEnv struct { 82 Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` 83 BlockScore *big.Int `json:"currentDifficulty" gencodec:"required"` 84 GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` 85 Number uint64 `json:"currentNumber" gencodec:"required"` 86 Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` 87 } 88 89 type stEnvMarshaling struct { 90 Coinbase common.UnprefixedAddress 91 BlockScore *math.HexOrDecimal256 92 GasLimit math.HexOrDecimal64 93 Number math.HexOrDecimal64 94 Timestamp math.HexOrDecimal64 95 } 96 97 //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go 98 99 type stTransaction struct { 100 GasPrice *big.Int `json:"gasPrice"` 101 Nonce uint64 `json:"nonce"` 102 To string `json:"to"` 103 Data []string `json:"data"` 104 GasLimit []uint64 `json:"gasLimit"` 105 Value []string `json:"value"` 106 PrivateKey []byte `json:"secretKey"` 107 } 108 109 type stTransactionMarshaling struct { 110 GasPrice *math.HexOrDecimal256 111 Nonce math.HexOrDecimal64 112 GasLimit []math.HexOrDecimal64 113 PrivateKey hexutil.Bytes 114 } 115 116 // getVMConfig takes a fork definition and returns a chain config. 117 // The fork definition can be 118 // - a plain forkname, e.g. `Byzantium`, 119 // - a fork basename, and a list of EIPs to enable; e.g. `Byzantium+1884+1283`. 120 func getVMConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) { 121 var ( 122 splitForks = strings.Split(forkString, "+") 123 ok bool 124 baseName, eipsStrings = splitForks[0], splitForks[1:] 125 ) 126 if baseConfig, ok = Forks[baseName]; !ok { 127 return nil, nil, UnsupportedForkError{baseName} 128 } 129 for _, eip := range eipsStrings { 130 if eipNum, err := strconv.Atoi(eip); err != nil { 131 return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum) 132 } else { 133 eips = append(eips, eipNum) 134 } 135 } 136 return baseConfig, eips, nil 137 } 138 139 // Subtests returns all valid subtests of the test. 140 func (t *StateTest) Subtests() []StateSubtest { 141 var sub []StateSubtest 142 for fork, pss := range t.json.Post { 143 for i := range pss { 144 sub = append(sub, StateSubtest{fork, i}) 145 } 146 } 147 return sub 148 } 149 150 // Run executes a specific subtest. 151 func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) { 152 config, eips, err := getVMConfig(subtest.Fork) 153 if err != nil { 154 return nil, UnsupportedForkError{subtest.Fork} 155 } 156 vmconfig.ExtraEips = eips 157 block := t.genesis(config).ToBlock(common.Hash{}, nil) 158 memDBManager := database.NewMemoryDBManager() 159 statedb := MakePreState(memDBManager, t.json.Pre) 160 161 post := t.json.Post[subtest.Fork][subtest.Index] 162 msg, err := t.json.Tx.toMessage(post, config.Rules(block.Number())) 163 if err != nil { 164 return nil, err 165 } 166 context := blockchain.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) 167 context.GetHash = vmTestBlockHash 168 evm := vm.NewEVM(context, statedb, config, &vmconfig) 169 170 snapshot := statedb.Snapshot() 171 if _, _, kerr := blockchain.ApplyMessage(evm, msg); kerr.ErrTxInvalid != nil { 172 statedb.RevertToSnapshot(snapshot) 173 } 174 if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { 175 return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) 176 } 177 178 root, _ := statedb.Commit(true) 179 // Add 0-value mining reward. This only makes a difference in the cases 180 // where 181 // - the coinbase suicided, or 182 // - there are only 'bad' transactions, which aren't executed. In those cases, 183 // the coinbase gets no txfee, so isn't created, and thus needs to be touched 184 statedb.AddBalance(block.Rewardbase(), new(big.Int)) 185 // And _now_ get the state root 186 root = statedb.IntermediateRoot(true) 187 188 if root != common.Hash(post.Root) { 189 return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) 190 } 191 return statedb, nil 192 } 193 194 func (t *StateTest) gasLimit(subtest StateSubtest) uint64 { 195 return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas] 196 } 197 198 func MakePreState(db database.DBManager, accounts blockchain.GenesisAlloc) *state.StateDB { 199 sdb := state.NewDatabase(db) 200 statedb, _ := state.New(common.Hash{}, sdb, nil) 201 for addr, a := range accounts { 202 if len(a.Code) != 0 { 203 statedb.SetCode(addr, a.Code) 204 } 205 for k, v := range a.Storage { 206 statedb.SetState(addr, k, v) 207 } 208 statedb.SetNonce(addr, a.Nonce) 209 statedb.SetBalance(addr, a.Balance) 210 } 211 // Commit and re-open to start with a clean state. 212 root, _ := statedb.Commit(false) 213 statedb, _ = state.New(root, sdb, nil) 214 return statedb 215 } 216 217 func (t *StateTest) genesis(config *params.ChainConfig) *blockchain.Genesis { 218 return &blockchain.Genesis{ 219 Config: config, 220 BlockScore: t.json.Env.BlockScore, 221 Number: t.json.Env.Number, 222 Timestamp: t.json.Env.Timestamp, 223 Alloc: t.json.Pre, 224 } 225 } 226 227 func (tx *stTransaction) toMessage(ps stPostState, r params.Rules) (blockchain.Message, error) { 228 // Derive sender from private key if present. 229 var from common.Address 230 if len(tx.PrivateKey) > 0 { 231 key, err := crypto.ToECDSA(tx.PrivateKey) 232 if err != nil { 233 return nil, fmt.Errorf("invalid private key: %v", err) 234 } 235 from = crypto.PubkeyToAddress(key.PublicKey) 236 } 237 // Parse recipient if present. 238 var to *common.Address 239 if tx.To != "" { 240 to = new(common.Address) 241 if err := to.UnmarshalText([]byte(tx.To)); err != nil { 242 return nil, fmt.Errorf("invalid to address: %v", err) 243 } 244 } 245 246 // Get values specific to this post state. 247 if ps.Indexes.Data > len(tx.Data) { 248 return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data) 249 } 250 if ps.Indexes.Value > len(tx.Value) { 251 return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value) 252 } 253 if ps.Indexes.Gas > len(tx.GasLimit) { 254 return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas) 255 } 256 dataHex := tx.Data[ps.Indexes.Data] 257 valueHex := tx.Value[ps.Indexes.Value] 258 gasLimit := tx.GasLimit[ps.Indexes.Gas] 259 // Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203 260 value := new(big.Int) 261 if valueHex != "0x" { 262 v, ok := math.ParseBig256(valueHex) 263 if !ok { 264 return nil, fmt.Errorf("invalid tx value %q", valueHex) 265 } 266 value = v 267 } 268 data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x")) 269 if err != nil { 270 return nil, fmt.Errorf("invalid tx data %q", dataHex) 271 } 272 273 intrinsicGas, err := types.IntrinsicGas(data, nil, to == nil, r) 274 if err != nil { 275 return nil, err 276 } 277 278 msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, intrinsicGas) 279 return msg, nil 280 } 281 282 func rlpHash(x interface{}) (h common.Hash) { 283 hw := sha3.NewKeccak256() 284 rlp.Encode(hw, x) 285 hw.Sum(h[:0]) 286 return h 287 }