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