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