gitlab.com/flarenetwork/coreth@v0.1.1/eth/tracers/tracers_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package tracers 28 29 import ( 30 "crypto/ecdsa" 31 "crypto/rand" 32 "encoding/json" 33 "io/ioutil" 34 "math/big" 35 "path/filepath" 36 "reflect" 37 "strings" 38 "testing" 39 40 "github.com/ethereum/go-ethereum/common" 41 "github.com/ethereum/go-ethereum/common/hexutil" 42 "github.com/ethereum/go-ethereum/common/math" 43 "github.com/ethereum/go-ethereum/crypto" 44 "github.com/ethereum/go-ethereum/rlp" 45 "gitlab.com/flarenetwork/coreth/core" 46 "gitlab.com/flarenetwork/coreth/core/rawdb" 47 "gitlab.com/flarenetwork/coreth/core/types" 48 "gitlab.com/flarenetwork/coreth/core/vm" 49 "gitlab.com/flarenetwork/coreth/params" 50 "gitlab.com/flarenetwork/coreth/tests" 51 ) 52 53 // To generate a new callTracer test, copy paste the makeTest method below into 54 // a Geth console and call it with a transaction hash you which to export. 55 56 /* 57 // makeTest generates a callTracer test by running a prestate reassembled and a 58 // call trace run, assembling all the gathered information into a test case. 59 var makeTest = function(tx, rewind) { 60 // Generate the genesis block from the block, transaction and prestate data 61 var block = eth.getBlock(eth.getTransaction(tx).blockHash); 62 var genesis = eth.getBlock(block.parentHash); 63 64 delete genesis.gasUsed; 65 delete genesis.logsBloom; 66 delete genesis.parentHash; 67 delete genesis.receiptsRoot; 68 delete genesis.sha3Uncles; 69 delete genesis.size; 70 delete genesis.transactions; 71 delete genesis.transactionsRoot; 72 delete genesis.uncles; 73 74 genesis.gasLimit = genesis.gasLimit.toString(); 75 genesis.number = genesis.number.toString(); 76 genesis.timestamp = genesis.timestamp.toString(); 77 78 genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind}); 79 for (var key in genesis.alloc) { 80 genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString(); 81 } 82 genesis.config = admin.nodeInfo.protocols.eth.config; 83 84 // Generate the call trace and produce the test input 85 var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind}); 86 delete result.time; 87 88 console.log(JSON.stringify({ 89 genesis: genesis, 90 context: { 91 number: block.number.toString(), 92 difficulty: block.difficulty, 93 timestamp: block.timestamp.toString(), 94 gasLimit: block.gasLimit.toString(), 95 miner: block.miner, 96 }, 97 input: eth.getRawTransaction(tx), 98 result: result, 99 }, null, 2)); 100 } 101 */ 102 103 // callTrace is the result of a callTracer run. 104 type callTrace struct { 105 Type string `json:"type"` 106 From common.Address `json:"from"` 107 To common.Address `json:"to"` 108 Input hexutil.Bytes `json:"input"` 109 Output hexutil.Bytes `json:"output"` 110 Gas *hexutil.Uint64 `json:"gas,omitempty"` 111 GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"` 112 Value *hexutil.Big `json:"value,omitempty"` 113 Error string `json:"error,omitempty"` 114 Calls []callTrace `json:"calls,omitempty"` 115 } 116 117 type callContext struct { 118 Number math.HexOrDecimal64 `json:"number"` 119 Difficulty *math.HexOrDecimal256 `json:"difficulty"` 120 Time math.HexOrDecimal64 `json:"timestamp"` 121 GasLimit math.HexOrDecimal64 `json:"gasLimit"` 122 Miner common.Address `json:"miner"` 123 } 124 125 // callTracerTest defines a single test to check the call tracer against. 126 type callTracerTest struct { 127 Genesis *core.Genesis `json:"genesis"` 128 Context *callContext `json:"context"` 129 Input string `json:"input"` 130 Result *callTrace `json:"result"` 131 } 132 133 func TestPrestateTracerCreate2(t *testing.T) { 134 unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), 135 new(big.Int), 5000000, big.NewInt(1), []byte{}) 136 137 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) 138 if err != nil { 139 t.Fatalf("err %v", err) 140 } 141 signer := types.NewEIP155Signer(big.NewInt(1)) 142 tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA) 143 if err != nil { 144 t.Fatalf("err %v", err) 145 } 146 /** 147 This comes from one of the test-vectors on the Skinny Create2 - EIP 148 149 address 0x00000000000000000000000000000000deadbeef 150 salt 0x00000000000000000000000000000000000000000000000000000000cafebabe 151 init_code 0xdeadbeef 152 gas (assuming no mem expansion): 32006 153 result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 154 */ 155 origin, _ := signer.Sender(tx) 156 txContext := vm.TxContext{ 157 Origin: origin, 158 GasPrice: big.NewInt(1), 159 } 160 context := vm.BlockContext{ 161 CanTransfer: core.CanTransfer, 162 Transfer: core.Transfer, 163 Coinbase: common.Address{}, 164 BlockNumber: new(big.Int).SetUint64(8000000), 165 Time: new(big.Int).SetUint64(5), 166 Difficulty: big.NewInt(0x30000), 167 GasLimit: uint64(6000000), 168 } 169 alloc := core.GenesisAlloc{} 170 171 // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns 172 // the address 173 alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{ 174 Nonce: 1, 175 Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"), 176 Balance: big.NewInt(1), 177 } 178 alloc[origin] = core.GenesisAccount{ 179 Nonce: 1, 180 Code: []byte{}, 181 Balance: big.NewInt(500000000000000), 182 } 183 _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) 184 185 // Create the tracer, the EVM environment and run it 186 tracer, err := New("prestateTracer", new(Context)) 187 if err != nil { 188 t.Fatalf("failed to create call tracer: %v", err) 189 } 190 evm := vm.NewEVM(context, txContext, statedb, params.AvalancheMainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) 191 192 msg, err := tx.AsMessage(signer, nil) 193 if err != nil { 194 t.Fatalf("failed to prepare transaction for tracing: %v", err) 195 } 196 st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) 197 if _, err = st.TransitionDb(); err != nil { 198 t.Fatalf("failed to execute transaction: %v", err) 199 } 200 // Retrieve the trace result and compare against the etalon 201 res, err := tracer.GetResult() 202 if err != nil { 203 t.Fatalf("failed to retrieve trace result: %v", err) 204 } 205 ret := make(map[string]interface{}) 206 if err := json.Unmarshal(res, &ret); err != nil { 207 t.Fatalf("failed to unmarshal trace result: %v", err) 208 } 209 if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has { 210 t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result") 211 } 212 } 213 214 // Iterates over all the input-output datasets in the tracer test harness and 215 // runs the JavaScript tracers against them. 216 func TestCallTracer(t *testing.T) { 217 files, err := ioutil.ReadDir("testdata") 218 if err != nil { 219 t.Fatalf("failed to retrieve tracer test suite: %v", err) 220 } 221 for _, file := range files { 222 if !strings.HasPrefix(file.Name(), "call_tracer_") { 223 continue 224 } 225 file := file // capture range variable 226 t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) { 227 t.Parallel() 228 229 // Call tracer test found, read if from disk 230 blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name())) 231 if err != nil { 232 t.Fatalf("failed to read testcase: %v", err) 233 } 234 test := new(callTracerTest) 235 if err := json.Unmarshal(blob, test); err != nil { 236 t.Fatalf("failed to parse testcase: %v", err) 237 } 238 // Configure a blockchain with the given prestate 239 tx := new(types.Transaction) 240 if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { 241 t.Fatalf("failed to parse testcase input: %v", err) 242 } 243 signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), new(big.Int).SetUint64(uint64(test.Context.Number))) 244 origin, _ := signer.Sender(tx) 245 txContext := vm.TxContext{ 246 Origin: origin, 247 GasPrice: tx.GasPrice(), 248 } 249 context := vm.BlockContext{ 250 CanTransfer: core.CanTransfer, 251 Transfer: core.Transfer, 252 Coinbase: test.Context.Miner, 253 BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), 254 Time: new(big.Int).SetUint64(uint64(test.Context.Time)), 255 Difficulty: (*big.Int)(test.Context.Difficulty), 256 GasLimit: uint64(test.Context.GasLimit), 257 } 258 _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) 259 260 // Create the tracer, the EVM environment and run it 261 tracer, err := New("callTracer", new(Context)) 262 if err != nil { 263 t.Fatalf("failed to create call tracer: %v", err) 264 } 265 evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) 266 267 msg, err := tx.AsMessage(signer, nil) 268 if err != nil { 269 t.Fatalf("failed to prepare transaction for tracing: %v", err) 270 } 271 st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) 272 if _, err = st.TransitionDb(); err != nil { 273 t.Fatalf("failed to execute transaction: %v", err) 274 } 275 // Retrieve the trace result and compare against the etalon 276 res, err := tracer.GetResult() 277 if err != nil { 278 t.Fatalf("failed to retrieve trace result: %v", err) 279 } 280 ret := new(callTrace) 281 if err := json.Unmarshal(res, ret); err != nil { 282 t.Fatalf("failed to unmarshal trace result: %v", err) 283 } 284 285 if !jsonEqual(ret, test.Result) { 286 // uncomment this for easier debugging 287 //have, _ := json.MarshalIndent(ret, "", " ") 288 //want, _ := json.MarshalIndent(test.Result, "", " ") 289 //t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", string(have), string(want)) 290 t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result) 291 } 292 }) 293 } 294 } 295 296 // jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to 297 // comparison 298 func jsonEqual(x, y interface{}) bool { 299 xTrace := new(callTrace) 300 yTrace := new(callTrace) 301 if xj, err := json.Marshal(x); err == nil { 302 json.Unmarshal(xj, xTrace) 303 } else { 304 return false 305 } 306 if yj, err := json.Marshal(y); err == nil { 307 json.Unmarshal(yj, yTrace) 308 } else { 309 return false 310 } 311 return reflect.DeepEqual(xTrace, yTrace) 312 }