tracers_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package tracers
  17. import (
  18. "encoding/json"
  19. "io/ioutil"
  20. "math/big"
  21. "path/filepath"
  22. "reflect"
  23. "strings"
  24. "testing"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/common/hexutil"
  27. "github.com/ethereum/go-ethereum/common/math"
  28. "github.com/ethereum/go-ethereum/core"
  29. "github.com/ethereum/go-ethereum/core/types"
  30. "github.com/ethereum/go-ethereum/core/vm"
  31. "github.com/ethereum/go-ethereum/ethdb"
  32. "github.com/ethereum/go-ethereum/rlp"
  33. "github.com/ethereum/go-ethereum/tests"
  34. )
  35. // To generate a new callTracer test, copy paste the makeTest method below into
  36. // a Geth console and call it with a transaction hash you which to export.
  37. /*
  38. // makeTest generates a callTracer test by running a prestate reassembled and a
  39. // call trace run, assembling all the gathered information into a test case.
  40. var makeTest = function(tx, rewind) {
  41. // Generate the genesis block from the block, transaction and prestate data
  42. var block = eth.getBlock(eth.getTransaction(tx).blockHash);
  43. var genesis = eth.getBlock(block.parentHash);
  44. delete genesis.gasUsed;
  45. delete genesis.logsBloom;
  46. delete genesis.parentHash;
  47. delete genesis.receiptsRoot;
  48. delete genesis.sha3Uncles;
  49. delete genesis.size;
  50. delete genesis.transactions;
  51. delete genesis.transactionsRoot;
  52. delete genesis.uncles;
  53. genesis.gasLimit = genesis.gasLimit.toString();
  54. genesis.number = genesis.number.toString();
  55. genesis.timestamp = genesis.timestamp.toString();
  56. genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
  57. for (var key in genesis.alloc) {
  58. genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
  59. }
  60. genesis.config = admin.nodeInfo.protocols.eth.config;
  61. // Generate the call trace and produce the test input
  62. var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
  63. delete result.time;
  64. console.log(JSON.stringify({
  65. genesis: genesis,
  66. context: {
  67. number: block.number.toString(),
  68. difficulty: block.difficulty,
  69. timestamp: block.timestamp.toString(),
  70. gasLimit: block.gasLimit.toString(),
  71. miner: block.miner,
  72. },
  73. input: eth.getRawTransaction(tx),
  74. result: result,
  75. }, null, 2));
  76. }
  77. */
  78. // callTrace is the result of a callTracer run.
  79. type callTrace struct {
  80. Type string `json:"type"`
  81. From common.Address `json:"from"`
  82. To common.Address `json:"to"`
  83. Input hexutil.Bytes `json:"input"`
  84. Output hexutil.Bytes `json:"output"`
  85. Gas *hexutil.Uint64 `json:"gas,omitempty"`
  86. GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
  87. Value *hexutil.Big `json:"value,omitempty"`
  88. Error string `json:"error,omitempty"`
  89. Calls []callTrace `json:"calls,omitempty"`
  90. }
  91. type callContext struct {
  92. Number math.HexOrDecimal64 `json:"number"`
  93. Difficulty *math.HexOrDecimal256 `json:"difficulty"`
  94. Time math.HexOrDecimal64 `json:"timestamp"`
  95. GasLimit math.HexOrDecimal64 `json:"gasLimit"`
  96. Miner common.Address `json:"miner"`
  97. }
  98. // callTracerTest defines a single test to check the call tracer against.
  99. type callTracerTest struct {
  100. Genesis *core.Genesis `json:"genesis"`
  101. Context *callContext `json:"context"`
  102. Input string `json:"input"`
  103. Result *callTrace `json:"result"`
  104. }
  105. // Iterates over all the input-output datasets in the tracer test harness and
  106. // runs the JavaScript tracers against them.
  107. func TestCallTracer(t *testing.T) {
  108. files, err := ioutil.ReadDir("testdata")
  109. if err != nil {
  110. t.Fatalf("failed to retrieve tracer test suite: %v", err)
  111. }
  112. for _, file := range files {
  113. if !strings.HasPrefix(file.Name(), "call_tracer_") {
  114. continue
  115. }
  116. file := file // capture range variable
  117. t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
  118. t.Parallel()
  119. // Call tracer test found, read if from disk
  120. blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
  121. if err != nil {
  122. t.Fatalf("failed to read testcase: %v", err)
  123. }
  124. test := new(callTracerTest)
  125. if err := json.Unmarshal(blob, test); err != nil {
  126. t.Fatalf("failed to parse testcase: %v", err)
  127. }
  128. // Configure a blockchain with the given prestate
  129. tx := new(types.Transaction)
  130. if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
  131. t.Fatalf("failed to parse testcase input: %v", err)
  132. }
  133. signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
  134. origin, _ := signer.Sender(tx)
  135. context := vm.Context{
  136. CanTransfer: core.CanTransfer,
  137. Transfer: core.Transfer,
  138. Origin: origin,
  139. Coinbase: test.Context.Miner,
  140. BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
  141. Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
  142. Difficulty: (*big.Int)(test.Context.Difficulty),
  143. GasLimit: uint64(test.Context.GasLimit),
  144. GasPrice: tx.GasPrice(),
  145. }
  146. statedb := tests.MakePreState(ethdb.NewMemDatabase(), test.Genesis.Alloc)
  147. // Create the tracer, the EVM environment and run it
  148. tracer, err := New("callTracer")
  149. if err != nil {
  150. t.Fatalf("failed to create call tracer: %v", err)
  151. }
  152. evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
  153. msg, err := tx.AsMessage(signer)
  154. if err != nil {
  155. t.Fatalf("failed to prepare transaction for tracing: %v", err)
  156. }
  157. st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
  158. if _, _, _, err = st.TransitionDb(); err != nil {
  159. t.Fatalf("failed to execute transaction: %v", err)
  160. }
  161. // Retrieve the trace result and compare against the etalon
  162. res, err := tracer.GetResult()
  163. if err != nil {
  164. t.Fatalf("failed to retrieve trace result: %v", err)
  165. }
  166. ret := new(callTrace)
  167. if err := json.Unmarshal(res, ret); err != nil {
  168. t.Fatalf("failed to unmarshal trace result: %v", err)
  169. }
  170. if !reflect.DeepEqual(ret, test.Result) {
  171. t.Fatalf("trace mismatch: have %+v, want %+v", ret, test.Result)
  172. }
  173. })
  174. }
  175. }