github.com/fuocnetworks/go-ethereum@v1.9.7/eth/tracers/tracers_test.go (about) 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 17 package tracers 18 19 import ( 20 "crypto/ecdsa" 21 "crypto/rand" 22 "encoding/json" 23 "io/ioutil" 24 "math/big" 25 "path/filepath" 26 "reflect" 27 "strings" 28 "testing" 29 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/common/hexutil" 32 "github.com/ethereum/go-ethereum/common/math" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/core/vm" 37 "github.com/ethereum/go-ethereum/crypto" 38 "github.com/ethereum/go-ethereum/params" 39 "github.com/ethereum/go-ethereum/rlp" 40 "github.com/ethereum/go-ethereum/tests" 41 ) 42 43 // To generate a new callTracer test, copy paste the makeTest method below into 44 // a Geth console and call it with a transaction hash you which to export. 45 46 /* 47 // makeTest generates a callTracer test by running a prestate reassembled and a 48 // call trace run, assembling all the gathered information into a test case. 49 var makeTest = function(tx, rewind) { 50 // Generate the genesis block from the block, transaction and prestate data 51 var block = eth.getBlock(eth.getTransaction(tx).blockHash); 52 var genesis = eth.getBlock(block.parentHash); 53 54 delete genesis.gasUsed; 55 delete genesis.logsBloom; 56 delete genesis.parentHash; 57 delete genesis.receiptsRoot; 58 delete genesis.sha3Uncles; 59 delete genesis.size; 60 delete genesis.transactions; 61 delete genesis.transactionsRoot; 62 delete genesis.uncles; 63 64 genesis.gasLimit = genesis.gasLimit.toString(); 65 genesis.number = genesis.number.toString(); 66 genesis.timestamp = genesis.timestamp.toString(); 67 68 genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind}); 69 for (var key in genesis.alloc) { 70 genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString(); 71 } 72 genesis.config = admin.nodeInfo.protocols.eth.config; 73 74 // Generate the call trace and produce the test input 75 var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind}); 76 delete result.time; 77 78 console.log(JSON.stringify({ 79 genesis: genesis, 80 context: { 81 number: block.number.toString(), 82 difficulty: block.difficulty, 83 timestamp: block.timestamp.toString(), 84 gasLimit: block.gasLimit.toString(), 85 miner: block.miner, 86 }, 87 input: eth.getRawTransaction(tx), 88 result: result, 89 }, null, 2)); 90 } 91 */ 92 93 // callTrace is the result of a callTracer run. 94 type callTrace struct { 95 Type string `json:"type"` 96 From common.Address `json:"from"` 97 To common.Address `json:"to"` 98 Input hexutil.Bytes `json:"input"` 99 Output hexutil.Bytes `json:"output"` 100 Gas *hexutil.Uint64 `json:"gas,omitempty"` 101 GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"` 102 Value *hexutil.Big `json:"value,omitempty"` 103 Error string `json:"error,omitempty"` 104 Calls []callTrace `json:"calls,omitempty"` 105 } 106 107 type callContext struct { 108 Number math.HexOrDecimal64 `json:"number"` 109 Difficulty *math.HexOrDecimal256 `json:"difficulty"` 110 Time math.HexOrDecimal64 `json:"timestamp"` 111 GasLimit math.HexOrDecimal64 `json:"gasLimit"` 112 Miner common.Address `json:"miner"` 113 } 114 115 // callTracerTest defines a single test to check the call tracer against. 116 type callTracerTest struct { 117 Genesis *core.Genesis `json:"genesis"` 118 Context *callContext `json:"context"` 119 Input string `json:"input"` 120 Result *callTrace `json:"result"` 121 } 122 123 func TestPrestateTracerCreate2(t *testing.T) { 124 unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), 125 new(big.Int), 5000000, big.NewInt(1), []byte{}) 126 127 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) 128 if err != nil { 129 t.Fatalf("err %v", err) 130 } 131 signer := types.NewEIP155Signer(big.NewInt(1)) 132 tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA) 133 if err != nil { 134 t.Fatalf("err %v", err) 135 } 136 /** 137 This comes from one of the test-vectors on the Skinny Create2 - EIP 138 139 address 0x00000000000000000000000000000000deadbeef 140 salt 0x00000000000000000000000000000000000000000000000000000000cafebabe 141 init_code 0xdeadbeef 142 gas (assuming no mem expansion): 32006 143 result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 144 */ 145 origin, _ := signer.Sender(tx) 146 context := vm.Context{ 147 CanTransfer: core.CanTransfer, 148 Transfer: core.Transfer, 149 Origin: origin, 150 Coinbase: common.Address{}, 151 BlockNumber: new(big.Int).SetUint64(8000000), 152 Time: new(big.Int).SetUint64(5), 153 Difficulty: big.NewInt(0x30000), 154 GasLimit: uint64(6000000), 155 GasPrice: big.NewInt(1), 156 } 157 alloc := core.GenesisAlloc{} 158 159 // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns 160 // the address 161 alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{ 162 Nonce: 1, 163 Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"), 164 Balance: big.NewInt(1), 165 } 166 alloc[origin] = core.GenesisAccount{ 167 Nonce: 1, 168 Code: []byte{}, 169 Balance: big.NewInt(500000000000000), 170 } 171 statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc) 172 173 // Create the tracer, the EVM environment and run it 174 tracer, err := New("prestateTracer") 175 if err != nil { 176 t.Fatalf("failed to create call tracer: %v", err) 177 } 178 evm := vm.NewEVM(context, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) 179 180 msg, err := tx.AsMessage(signer) 181 if err != nil { 182 t.Fatalf("failed to prepare transaction for tracing: %v", err) 183 } 184 st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) 185 if _, _, _, err = st.TransitionDb(); err != nil { 186 t.Fatalf("failed to execute transaction: %v", err) 187 } 188 // Retrieve the trace result and compare against the etalon 189 res, err := tracer.GetResult() 190 if err != nil { 191 t.Fatalf("failed to retrieve trace result: %v", err) 192 } 193 ret := make(map[string]interface{}) 194 if err := json.Unmarshal(res, &ret); err != nil { 195 t.Fatalf("failed to unmarshal trace result: %v", err) 196 } 197 if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has { 198 t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result") 199 } 200 } 201 202 // Iterates over all the input-output datasets in the tracer test harness and 203 // runs the JavaScript tracers against them. 204 func TestCallTracer(t *testing.T) { 205 files, err := ioutil.ReadDir("testdata") 206 if err != nil { 207 t.Fatalf("failed to retrieve tracer test suite: %v", err) 208 } 209 for _, file := range files { 210 if !strings.HasPrefix(file.Name(), "call_tracer_") { 211 continue 212 } 213 file := file // capture range variable 214 t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) { 215 t.Parallel() 216 217 // Call tracer test found, read if from disk 218 blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name())) 219 if err != nil { 220 t.Fatalf("failed to read testcase: %v", err) 221 } 222 test := new(callTracerTest) 223 if err := json.Unmarshal(blob, test); err != nil { 224 t.Fatalf("failed to parse testcase: %v", err) 225 } 226 // Configure a blockchain with the given prestate 227 tx := new(types.Transaction) 228 if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { 229 t.Fatalf("failed to parse testcase input: %v", err) 230 } 231 signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) 232 origin, _ := signer.Sender(tx) 233 234 context := vm.Context{ 235 CanTransfer: core.CanTransfer, 236 Transfer: core.Transfer, 237 Origin: origin, 238 Coinbase: test.Context.Miner, 239 BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), 240 Time: new(big.Int).SetUint64(uint64(test.Context.Time)), 241 Difficulty: (*big.Int)(test.Context.Difficulty), 242 GasLimit: uint64(test.Context.GasLimit), 243 GasPrice: tx.GasPrice(), 244 } 245 statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) 246 247 // Create the tracer, the EVM environment and run it 248 tracer, err := New("callTracer") 249 if err != nil { 250 t.Fatalf("failed to create call tracer: %v", err) 251 } 252 evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) 253 254 msg, err := tx.AsMessage(signer) 255 if err != nil { 256 t.Fatalf("failed to prepare transaction for tracing: %v", err) 257 } 258 st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) 259 if _, _, _, err = st.TransitionDb(); err != nil { 260 t.Fatalf("failed to execute transaction: %v", err) 261 } 262 // Retrieve the trace result and compare against the etalon 263 res, err := tracer.GetResult() 264 if err != nil { 265 t.Fatalf("failed to retrieve trace result: %v", err) 266 } 267 ret := new(callTrace) 268 if err := json.Unmarshal(res, ret); err != nil { 269 t.Fatalf("failed to unmarshal trace result: %v", err) 270 } 271 272 if !reflect.DeepEqual(ret, test.Result) { 273 t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result) 274 } 275 }) 276 } 277 }