github.com/klaytn/klaytn@v1.10.2/node/cn/tracers/tracers_test.go (about) 1 // Copyright 2018 The klaytn Authors 2 // Copyright 2017 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 eth/tracers/tracers_test.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package tracers 22 23 import ( 24 "crypto/ecdsa" 25 "crypto/rand" 26 "encoding/json" 27 "io/ioutil" 28 "math/big" 29 "path/filepath" 30 "strings" 31 "testing" 32 33 "github.com/klaytn/klaytn/blockchain" 34 "github.com/klaytn/klaytn/blockchain/types" 35 "github.com/klaytn/klaytn/blockchain/vm" 36 "github.com/klaytn/klaytn/common" 37 "github.com/klaytn/klaytn/common/hexutil" 38 "github.com/klaytn/klaytn/common/math" 39 "github.com/klaytn/klaytn/crypto" 40 "github.com/klaytn/klaytn/fork" 41 "github.com/klaytn/klaytn/params" 42 "github.com/klaytn/klaytn/rlp" 43 "github.com/klaytn/klaytn/storage/database" 44 "github.com/klaytn/klaytn/tests" 45 "github.com/stretchr/testify/assert" 46 "github.com/stretchr/testify/require" 47 ) 48 49 // To generate a new callTracer test, use the `make_testdata.sh` script. 50 51 type reverted struct { 52 Contract *common.Address `json:"contract"` 53 Message string `json:"message"` 54 } 55 56 // callTrace is the result of a callTracer run. 57 type callTrace struct { 58 Type string `json:"type"` 59 From *common.Address `json:"from"` 60 To *common.Address `json:"to"` 61 Input hexutil.Bytes `json:"input"` 62 Output hexutil.Bytes `json:"output"` 63 Gas hexutil.Uint64 `json:"gas,omitempty"` 64 GasUsed hexutil.Uint64 `json:"gasUsed,omitempty"` 65 Value hexutil.Uint64 `json:"value,omitempty"` 66 Error string `json:"error,omitempty"` 67 Calls []callTrace `json:"calls,omitempty"` 68 Reverted *reverted `json:"reverted,omitempty"` 69 } 70 71 type callContext struct { 72 Number math.HexOrDecimal64 `json:"number"` 73 BlockScore *math.HexOrDecimal256 `json:"blockScore"` 74 Time math.HexOrDecimal64 `json:"timestamp"` 75 GasLimit math.HexOrDecimal64 `json:"gasLimit"` 76 Miner common.Address `json:"miner"` 77 } 78 79 // callTracerTest defines a single test to check the call tracer against. 80 type callTracerTest struct { 81 Genesis *blockchain.Genesis `json:"genesis"` 82 Context *callContext `json:"context"` 83 Input string `json:"input,omitempty"` 84 Transaction map[string]string `json:"transaction,omitempty"` 85 Result *callTrace `json:"result"` 86 } 87 88 func TestPrestateTracerCreate2(t *testing.T) { 89 unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), 90 new(big.Int), 5000000, big.NewInt(1), []byte{}) 91 92 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) 93 if err != nil { 94 t.Fatalf("err %v", err) 95 } 96 signer := types.LatestSignerForChainID(big.NewInt(1)) 97 tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA) 98 if err != nil { 99 t.Fatalf("err %v", err) 100 } 101 /** 102 This comes from one of the test-vectors on the Skinny Create2 - EIP 103 address 0x00000000000000000000000000000000deadbeef 104 salt 0x00000000000000000000000000000000000000000000000000000000cafebabe 105 init_code 0xdeadbeef 106 gas (assuming no mem expansion): 32006 107 result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 108 */ 109 origin, _ := signer.Sender(tx) 110 context := vm.Context{ 111 CanTransfer: blockchain.CanTransfer, 112 Transfer: blockchain.Transfer, 113 Origin: origin, 114 Coinbase: common.Address{}, 115 BlockNumber: new(big.Int).SetUint64(8000000), 116 Time: new(big.Int).SetUint64(5), 117 BlockScore: big.NewInt(0x30000), 118 GasLimit: uint64(6000000), 119 GasPrice: big.NewInt(1), 120 } 121 alloc := blockchain.GenesisAlloc{} 122 // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns 123 // the address 124 alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = blockchain.GenesisAccount{ 125 Nonce: 1, 126 Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"), 127 Balance: big.NewInt(1), 128 } 129 alloc[origin] = blockchain.GenesisAccount{ 130 Nonce: 1, 131 Code: []byte{}, 132 Balance: big.NewInt(500000000000000), 133 } 134 statedb := tests.MakePreState(database.NewMemoryDBManager(), alloc) 135 // Create the tracer, the EVM environment and run it 136 tracer, err := New("prestateTracer", false) 137 if err != nil { 138 t.Fatalf("failed to create call tracer: %v", err) 139 } 140 evm := vm.NewEVM(context, statedb, params.CypressChainConfig, &vm.Config{Debug: true, Tracer: tracer}) 141 142 fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{}) 143 msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, context.BlockNumber.Uint64()) 144 if err != nil { 145 t.Fatalf("failed to prepare transaction for tracing: %v", err) 146 } 147 st := blockchain.NewStateTransition(evm, msg) 148 if _, _, kerr := st.TransitionDb(); kerr.ErrTxInvalid != nil { 149 t.Fatalf("failed to execute transaction: %v", kerr.ErrTxInvalid) 150 } 151 // Retrieve the trace result and compare against the etalon 152 res, err := tracer.GetResult() 153 if err != nil { 154 t.Fatalf("failed to retrieve trace result: %v", err) 155 } 156 ret := make(map[string]interface{}) 157 if err := json.Unmarshal(res, &ret); err != nil { 158 t.Fatalf("failed to unmarshal trace result: %v", err) 159 } 160 if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has { 161 t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result") 162 } 163 } 164 165 func covertToCallTrace(t *testing.T, internalTx *vm.InternalTxTrace) *callTrace { 166 // coverts nested InternalTxTraces 167 var nestedCalls []callTrace 168 for _, call := range internalTx.Calls { 169 nestedCalls = append(nestedCalls, *covertToCallTrace(t, call)) 170 } 171 172 // decodes input and output if they are not an empty string 173 var decodedInput []byte 174 var decodedOutput []byte 175 var err error 176 if internalTx.Input != "" { 177 decodedInput, err = hexutil.Decode(internalTx.Input) 178 if err != nil { 179 t.Fatal("failed to decode input of an internal transaction", "err", err) 180 } 181 } 182 if internalTx.Output != "" { 183 decodedOutput, err = hexutil.Decode(internalTx.Output) 184 if err != nil { 185 t.Fatal("failed to decode output of an internal transaction", "err", err) 186 } 187 } 188 189 // decodes value into *big.Int if it is not an empty string 190 var value *big.Int 191 if internalTx.Value != "" { 192 value, err = hexutil.DecodeBig(internalTx.Value) 193 if err != nil { 194 t.Fatal("failed to decode value of an internal transaction", "err", err) 195 } 196 } 197 var val hexutil.Uint64 198 if value != nil { 199 val = hexutil.Uint64(value.Uint64()) 200 } 201 202 errStr := "" 203 if internalTx.Error != nil { 204 errStr = internalTx.Error.Error() 205 } 206 207 var revertedInfo *reverted 208 if internalTx.Reverted != nil { 209 revertedInfo = &reverted{ 210 Contract: internalTx.Reverted.Contract, 211 Message: internalTx.Reverted.Message, 212 } 213 } 214 215 ct := &callTrace{ 216 Type: internalTx.Type, 217 From: internalTx.From, 218 To: internalTx.To, 219 Input: decodedInput, 220 Output: decodedOutput, 221 Gas: hexutil.Uint64(internalTx.Gas), 222 GasUsed: hexutil.Uint64(internalTx.GasUsed), 223 Value: val, 224 Error: errStr, 225 Calls: nestedCalls, 226 Reverted: revertedInfo, 227 } 228 229 return ct 230 } 231 232 // Iterates over all the input-output datasets in the tracer test harness and 233 // runs the JavaScript tracers against them. 234 func TestCallTracer(t *testing.T) { 235 files, err := ioutil.ReadDir("testdata") 236 if err != nil { 237 t.Fatalf("failed to retrieve tracer test suite: %v", err) 238 } 239 for _, file := range files { 240 if !strings.HasPrefix(file.Name(), "call_tracer_") { 241 continue 242 } 243 file := file // capture range variable 244 t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) { 245 // t.Parallel() 246 247 // Call tracer test found, read if from disk 248 blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name())) 249 if err != nil { 250 t.Fatalf("failed to read testcase: %v", err) 251 } 252 test := new(callTracerTest) 253 if err := json.Unmarshal(blob, test); err != nil { 254 t.Fatalf("failed to parse testcase: %v", err) 255 } 256 257 signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) 258 tx := new(types.Transaction) 259 // Configure a blockchain with the given prestate 260 if test.Input != "" { 261 if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { 262 t.Fatalf("failed to parse testcase input: %v", err) 263 } 264 } else { 265 // Configure a blockchain with the given prestate 266 value := new(big.Int) 267 gasPrice := new(big.Int) 268 err = value.UnmarshalJSON([]byte(test.Transaction["value"])) 269 require.NoError(t, err) 270 err = gasPrice.UnmarshalJSON([]byte(test.Transaction["gasPrice"])) 271 require.NoError(t, err) 272 nonce, b := math.ParseUint64(test.Transaction["nonce"]) 273 require.True(t, b) 274 gas, b := math.ParseUint64(test.Transaction["gas"]) 275 require.True(t, b) 276 277 to := common.HexToAddress(test.Transaction["to"]) 278 input := common.FromHex(test.Transaction["input"]) 279 280 tx = types.NewTransaction(nonce, to, value, gas, gasPrice, input) 281 282 testKey, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 283 require.NoError(t, err) 284 err = tx.Sign(signer, testKey) 285 require.NoError(t, err) 286 } 287 288 origin, _ := signer.Sender(tx) 289 290 context := vm.Context{ 291 CanTransfer: blockchain.CanTransfer, 292 Transfer: blockchain.Transfer, 293 Origin: origin, 294 BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), 295 Time: new(big.Int).SetUint64(uint64(test.Context.Time)), 296 BlockScore: (*big.Int)(test.Context.BlockScore), 297 GasLimit: uint64(test.Context.GasLimit), 298 GasPrice: tx.GasPrice(), 299 } 300 statedb := tests.MakePreState(database.NewMemoryDBManager(), test.Genesis.Alloc) 301 302 // Create the tracer, the EVM environment and run it 303 tracer, err := New("callTracer", false) 304 if err != nil { 305 t.Fatalf("failed to create call tracer: %v", err) 306 } 307 evm := vm.NewEVM(context, statedb, test.Genesis.Config, &vm.Config{Debug: true, Tracer: tracer}) 308 309 fork.SetHardForkBlockNumberConfig(test.Genesis.Config) 310 msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, context.BlockNumber.Uint64()) 311 if err != nil { 312 t.Fatalf("failed to prepare transaction for tracing: %v", err) 313 } 314 st := blockchain.NewStateTransition(evm, msg) 315 if _, _, kerr := st.TransitionDb(); kerr.ErrTxInvalid != nil { 316 t.Fatalf("failed to execute transaction: %v", kerr.ErrTxInvalid) 317 } 318 // Retrieve the trace result and compare against the etalon 319 res, err := tracer.GetResult() 320 if err != nil { 321 t.Fatalf("failed to retrieve trace result: %v", err) 322 } 323 ret := new(callTrace) 324 if err := json.Unmarshal(res, ret); err != nil { 325 t.Fatalf("failed to unmarshal trace result: %v", err) 326 } 327 jsonEqual(t, ret, test.Result) 328 }) 329 } 330 } 331 332 // Compare JSON representations for human-friendly diffs. 333 func jsonEqual(t *testing.T, x, y interface{}) { 334 xj, err := json.MarshalIndent(x, "", " ") 335 assert.Nil(t, err) 336 337 yj, err := json.MarshalIndent(y, "", " ") 338 assert.Nil(t, err) 339 340 assert.Equal(t, string(xj), string(yj)) 341 } 342 343 // Iterates over all the input-output datasets in the tracer test harness and 344 // runs the InternalCallTracer against them. 345 func TestInternalCallTracer(t *testing.T) { 346 files, err := ioutil.ReadDir("testdata") 347 if err != nil { 348 t.Fatalf("failed to retrieve tracer test suite: %v", err) 349 } 350 for _, file := range files { 351 if !strings.HasPrefix(file.Name(), "call_tracer_") { 352 continue 353 } 354 file := file // capture range variable 355 t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) { 356 // t.Parallel() 357 358 // Call tracer test found, read if from disk 359 blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name())) 360 if err != nil { 361 t.Fatalf("failed to read testcase: %v", err) 362 } 363 test := new(callTracerTest) 364 if err := json.Unmarshal(blob, test); err != nil { 365 t.Fatalf("failed to parse testcase: %v", err) 366 } 367 368 signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) 369 tx := new(types.Transaction) 370 // Configure a blockchain with the given prestate 371 if test.Input != "" { 372 if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { 373 t.Fatalf("failed to parse testcase input: %v", err) 374 } 375 } else { 376 // Configure a blockchain with the given prestate 377 value := new(big.Int) 378 gasPrice := new(big.Int) 379 err = value.UnmarshalJSON([]byte(test.Transaction["value"])) 380 require.NoError(t, err) 381 err = gasPrice.UnmarshalJSON([]byte(test.Transaction["gasPrice"])) 382 require.NoError(t, err) 383 nonce, b := math.ParseUint64(test.Transaction["nonce"]) 384 require.True(t, b) 385 gas, b := math.ParseUint64(test.Transaction["gas"]) 386 require.True(t, b) 387 388 to := common.HexToAddress(test.Transaction["to"]) 389 input := common.FromHex(test.Transaction["input"]) 390 391 tx = types.NewTransaction(nonce, to, value, gas, gasPrice, input) 392 393 testKey, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 394 require.NoError(t, err) 395 err = tx.Sign(signer, testKey) 396 require.NoError(t, err) 397 } 398 399 origin, _ := signer.Sender(tx) 400 401 context := vm.Context{ 402 CanTransfer: blockchain.CanTransfer, 403 Transfer: blockchain.Transfer, 404 Origin: origin, 405 BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), 406 Time: new(big.Int).SetUint64(uint64(test.Context.Time)), 407 BlockScore: (*big.Int)(test.Context.BlockScore), 408 GasLimit: uint64(test.Context.GasLimit), 409 GasPrice: tx.GasPrice(), 410 } 411 statedb := tests.MakePreState(database.NewMemoryDBManager(), test.Genesis.Alloc) 412 413 // Create the tracer, the EVM environment and run it 414 tracer := vm.NewInternalTxTracer() 415 evm := vm.NewEVM(context, statedb, test.Genesis.Config, &vm.Config{Debug: true, Tracer: tracer}) 416 417 fork.SetHardForkBlockNumberConfig(test.Genesis.Config) 418 msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, context.BlockNumber.Uint64()) 419 if err != nil { 420 t.Fatalf("failed to prepare transaction for tracing: %v", err) 421 } 422 st := blockchain.NewStateTransition(evm, msg) 423 if _, _, kerr := st.TransitionDb(); kerr.ErrTxInvalid != nil { 424 t.Fatalf("failed to execute transaction: %v", kerr.ErrTxInvalid) 425 } 426 // Retrieve the trace result and compare against the etalon 427 res, err := tracer.GetResult() 428 if err != nil { 429 t.Fatalf("failed to retrieve trace result: %v", err) 430 } 431 432 resultFromInternalCallTracer := covertToCallTrace(t, res) 433 jsonEqual(t, test.Result, resultFromInternalCallTracer) 434 }) 435 } 436 }