github.com/juliankolbe/go-ethereum@v1.9.992/eth/tracers/api.go (about) 1 // Copyright 2021 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 "bufio" 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "runtime" 28 "sync" 29 "time" 30 31 "github.com/juliankolbe/go-ethereum/common" 32 "github.com/juliankolbe/go-ethereum/common/hexutil" 33 "github.com/juliankolbe/go-ethereum/consensus" 34 "github.com/juliankolbe/go-ethereum/core" 35 "github.com/juliankolbe/go-ethereum/core/rawdb" 36 "github.com/juliankolbe/go-ethereum/core/state" 37 "github.com/juliankolbe/go-ethereum/core/types" 38 "github.com/juliankolbe/go-ethereum/core/vm" 39 "github.com/juliankolbe/go-ethereum/ethdb" 40 "github.com/juliankolbe/go-ethereum/internal/ethapi" 41 "github.com/juliankolbe/go-ethereum/log" 42 "github.com/juliankolbe/go-ethereum/params" 43 "github.com/juliankolbe/go-ethereum/rlp" 44 "github.com/juliankolbe/go-ethereum/rpc" 45 ) 46 47 const ( 48 // defaultTraceTimeout is the amount of time a single transaction can execute 49 // by default before being forcefully aborted. 50 defaultTraceTimeout = 5 * time.Second 51 52 // defaultTraceReexec is the number of blocks the tracer is willing to go back 53 // and reexecute to produce missing historical state necessary to run a specific 54 // trace. 55 defaultTraceReexec = uint64(128) 56 ) 57 58 // Backend interface provides the common API services (that are provided by 59 // both full and light clients) with access to necessary functions. 60 type Backend interface { 61 HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) 62 HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) 63 BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) 64 BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) 65 GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) 66 RPCGasCap() uint64 67 ChainConfig() *params.ChainConfig 68 Engine() consensus.Engine 69 ChainDb() ethdb.Database 70 StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) 71 StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) 72 StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) 73 } 74 75 // API is the collection of tracing APIs exposed over the private debugging endpoint. 76 type API struct { 77 backend Backend 78 } 79 80 // NewAPI creates a new API definition for the tracing methods of the Ethereum service. 81 func NewAPI(backend Backend) *API { 82 return &API{backend: backend} 83 } 84 85 type chainContext struct { 86 api *API 87 ctx context.Context 88 } 89 90 func (context *chainContext) Engine() consensus.Engine { 91 return context.api.backend.Engine() 92 } 93 94 func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { 95 header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number)) 96 if err != nil { 97 return nil 98 } 99 if header.Hash() == hash { 100 return header 101 } 102 header, err = context.api.backend.HeaderByHash(context.ctx, hash) 103 if err != nil { 104 return nil 105 } 106 return header 107 } 108 109 // chainContext construts the context reader which is used by the evm for reading 110 // the necessary chain context. 111 func (api *API) chainContext(ctx context.Context) core.ChainContext { 112 return &chainContext{api: api, ctx: ctx} 113 } 114 115 // blockByNumber is the wrapper of the chain access function offered by the backend. 116 // It will return an error if the block is not found. 117 func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 118 block, err := api.backend.BlockByNumber(ctx, number) 119 if err != nil { 120 return nil, err 121 } 122 if block == nil { 123 return nil, fmt.Errorf("block #%d not found", number) 124 } 125 return block, nil 126 } 127 128 // blockByHash is the wrapper of the chain access function offered by the backend. 129 // It will return an error if the block is not found. 130 func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 131 block, err := api.backend.BlockByHash(ctx, hash) 132 if err != nil { 133 return nil, err 134 } 135 if block == nil { 136 return nil, fmt.Errorf("block %s not found", hash.Hex()) 137 } 138 return block, nil 139 } 140 141 // blockByNumberAndHash is the wrapper of the chain access function offered by 142 // the backend. It will return an error if the block is not found. 143 // 144 // Note this function is friendly for the light client which can only retrieve the 145 // historical(before the CHT) header/block by number. 146 func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { 147 block, err := api.blockByNumber(ctx, number) 148 if err != nil { 149 return nil, err 150 } 151 if block.Hash() == hash { 152 return block, nil 153 } 154 return api.blockByHash(ctx, hash) 155 } 156 157 // TraceConfig holds extra parameters to trace functions. 158 type TraceConfig struct { 159 *vm.LogConfig 160 Tracer *string 161 Timeout *string 162 Reexec *uint64 163 } 164 165 // StdTraceConfig holds extra parameters to standard-json trace functions. 166 type StdTraceConfig struct { 167 vm.LogConfig 168 Reexec *uint64 169 TxHash common.Hash 170 } 171 172 // txTraceResult is the result of a single transaction trace. 173 type txTraceResult struct { 174 Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer 175 Error string `json:"error,omitempty"` // Trace failure produced by the tracer 176 } 177 178 // blockTraceTask represents a single block trace task when an entire chain is 179 // being traced. 180 type blockTraceTask struct { 181 statedb *state.StateDB // Intermediate state prepped for tracing 182 block *types.Block // Block to trace the transactions from 183 results []*txTraceResult // Trace results procudes by the task 184 } 185 186 // blockTraceResult represets the results of tracing a single block when an entire 187 // chain is being traced. 188 type blockTraceResult struct { 189 Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace 190 Hash common.Hash `json:"hash"` // Block hash corresponding to this trace 191 Traces []*txTraceResult `json:"traces"` // Trace results produced by the task 192 } 193 194 // txTraceTask represents a single transaction trace task when an entire block 195 // is being traced. 196 type txTraceTask struct { 197 statedb *state.StateDB // Intermediate state prepped for tracing 198 index int // Transaction offset in the block 199 } 200 201 // TraceChain returns the structured logs created during the execution of EVM 202 // between two blocks (excluding start) and returns them as a JSON object. 203 func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { // Fetch the block interval that we want to trace 204 from, err := api.blockByNumber(ctx, start) 205 if err != nil { 206 return nil, err 207 } 208 to, err := api.blockByNumber(ctx, end) 209 if err != nil { 210 return nil, err 211 } 212 if from.Number().Cmp(to.Number()) >= 0 { 213 return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) 214 } 215 return api.traceChain(ctx, from, to, config) 216 } 217 218 // traceChain configures a new tracer according to the provided configuration, and 219 // executes all the transactions contained within. The return value will be one item 220 // per transaction, dependent on the requested tracer. 221 func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { 222 // Tracing a chain is a **long** operation, only do with subscriptions 223 notifier, supported := rpc.NotifierFromContext(ctx) 224 if !supported { 225 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 226 } 227 sub := notifier.CreateSubscription() 228 229 // Shift the border to a block ahead in order to get the states 230 // before these blocks. 231 endBlock, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(end.NumberU64()-1), end.ParentHash()) 232 if err != nil { 233 return nil, err 234 } 235 // Prepare all the states for tracing. Note this procedure can take very 236 // long time. Timeout mechanism is necessary. 237 reexec := defaultTraceReexec 238 if config != nil && config.Reexec != nil { 239 reexec = *config.Reexec 240 } 241 states, release, err := api.backend.StatesInRange(ctx, start, endBlock, reexec) 242 if err != nil { 243 return nil, err 244 } 245 defer release() // Release all the resources in the last step. 246 247 blocks := int(end.NumberU64() - start.NumberU64()) 248 threads := runtime.NumCPU() 249 if threads > blocks { 250 threads = blocks 251 } 252 var ( 253 pend = new(sync.WaitGroup) 254 tasks = make(chan *blockTraceTask, threads) 255 results = make(chan *blockTraceTask, threads) 256 ) 257 for th := 0; th < threads; th++ { 258 pend.Add(1) 259 go func() { 260 defer pend.Done() 261 262 // Fetch and execute the next block trace tasks 263 for task := range tasks { 264 signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) 265 blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil) 266 // Trace all the transactions contained within 267 for i, tx := range task.block.Transactions() { 268 msg, _ := tx.AsMessage(signer) 269 res, err := api.traceTx(ctx, msg, blockCtx, task.statedb, config) 270 if err != nil { 271 task.results[i] = &txTraceResult{Error: err.Error()} 272 log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) 273 break 274 } 275 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 276 task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number())) 277 task.results[i] = &txTraceResult{Result: res} 278 } 279 // Stream the result back to the user or abort on teardown 280 select { 281 case results <- task: 282 case <-notifier.Closed(): 283 return 284 } 285 } 286 }() 287 } 288 // Start a goroutine to feed all the blocks into the tracers 289 begin := time.Now() 290 291 go func() { 292 var ( 293 logged time.Time 294 number uint64 295 traced uint64 296 failed error 297 ) 298 // Ensure everything is properly cleaned up on any exit path 299 defer func() { 300 close(tasks) 301 pend.Wait() 302 303 switch { 304 case failed != nil: 305 log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed) 306 case number < end.NumberU64(): 307 log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin)) 308 default: 309 log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) 310 } 311 close(results) 312 }() 313 // Feed all the blocks both into the tracer, as well as fast process concurrently 314 for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ { 315 // Stop tracing if interruption was requested 316 select { 317 case <-notifier.Closed(): 318 return 319 default: 320 } 321 // Print progress logs if long enough time elapsed 322 if time.Since(logged) > 8*time.Second { 323 logged = time.Now() 324 log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) 325 } 326 // Retrieve the next block to trace 327 block, err := api.blockByNumber(ctx, rpc.BlockNumber(number)) 328 if err != nil { 329 failed = err 330 break 331 } 332 // Send the block over to the concurrent tracers (if not in the fast-forward phase) 333 txs := block.Transactions() 334 select { 335 case tasks <- &blockTraceTask{statedb: states[int(number-start.NumberU64()-1)], block: block, results: make([]*txTraceResult, len(txs))}: 336 case <-notifier.Closed(): 337 return 338 } 339 traced += uint64(len(txs)) 340 } 341 }() 342 343 // Keep reading the trace results and stream the to the user 344 go func() { 345 var ( 346 done = make(map[uint64]*blockTraceResult) 347 next = start.NumberU64() + 1 348 ) 349 for res := range results { 350 // Queue up next received result 351 result := &blockTraceResult{ 352 Block: hexutil.Uint64(res.block.NumberU64()), 353 Hash: res.block.Hash(), 354 Traces: res.results, 355 } 356 done[uint64(result.Block)] = result 357 358 // Stream completed traces to the user, aborting on the first error 359 for result, ok := done[next]; ok; result, ok = done[next] { 360 if len(result.Traces) > 0 || next == end.NumberU64() { 361 notifier.Notify(sub.ID, result) 362 } 363 delete(done, next) 364 next++ 365 } 366 } 367 }() 368 return sub, nil 369 } 370 371 // TraceBlockByNumber returns the structured logs created during the execution of 372 // EVM and returns them as a JSON object. 373 func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { 374 block, err := api.blockByNumber(ctx, number) 375 if err != nil { 376 return nil, err 377 } 378 return api.traceBlock(ctx, block, config) 379 } 380 381 // TraceBlockByHash returns the structured logs created during the execution of 382 // EVM and returns them as a JSON object. 383 func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 384 block, err := api.blockByHash(ctx, hash) 385 if err != nil { 386 return nil, err 387 } 388 return api.traceBlock(ctx, block, config) 389 } 390 391 // TraceBlock returns the structured logs created during the execution of EVM 392 // and returns them as a JSON object. 393 func (api *API) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { 394 block := new(types.Block) 395 if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { 396 return nil, fmt.Errorf("could not decode block: %v", err) 397 } 398 return api.traceBlock(ctx, block, config) 399 } 400 401 // TraceBlockFromFile returns the structured logs created during the execution of 402 // EVM and returns them as a JSON object. 403 func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { 404 blob, err := ioutil.ReadFile(file) 405 if err != nil { 406 return nil, fmt.Errorf("could not read file: %v", err) 407 } 408 return api.TraceBlock(ctx, blob, config) 409 } 410 411 // TraceBadBlock returns the structured logs created during the execution of 412 // EVM against a block pulled from the pool of bad ones and returns them as a JSON 413 // object. 414 func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 415 for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { 416 if block.Hash() == hash { 417 return api.traceBlock(ctx, block, config) 418 } 419 } 420 return nil, fmt.Errorf("bad block %#x not found", hash) 421 } 422 423 // StandardTraceBlockToFile dumps the structured logs created during the 424 // execution of EVM to the local file system and returns a list of files 425 // to the caller. 426 func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 427 block, err := api.blockByHash(ctx, hash) 428 if err != nil { 429 return nil, err 430 } 431 return api.standardTraceBlockToFile(ctx, block, config) 432 } 433 434 // StandardTraceBadBlockToFile dumps the structured logs created during the 435 // execution of EVM against a block pulled from the pool of bad ones to the 436 // local file system and returns a list of files to the caller. 437 func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 438 for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { 439 if block.Hash() == hash { 440 return api.standardTraceBlockToFile(ctx, block, config) 441 } 442 } 443 return nil, fmt.Errorf("bad block %#x not found", hash) 444 } 445 446 // traceBlock configures a new tracer according to the provided configuration, and 447 // executes all the transactions contained within. The return value will be one item 448 // per transaction, dependent on the requestd tracer. 449 func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { 450 if block.NumberU64() == 0 { 451 return nil, errors.New("genesis is not traceable") 452 } 453 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 454 if err != nil { 455 return nil, err 456 } 457 reexec := defaultTraceReexec 458 if config != nil && config.Reexec != nil { 459 reexec = *config.Reexec 460 } 461 statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec) 462 if err != nil { 463 return nil, err 464 } 465 defer release() 466 467 // Execute all the transaction contained within the block concurrently 468 var ( 469 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) 470 txs = block.Transactions() 471 results = make([]*txTraceResult, len(txs)) 472 473 pend = new(sync.WaitGroup) 474 jobs = make(chan *txTraceTask, len(txs)) 475 ) 476 threads := runtime.NumCPU() 477 if threads > len(txs) { 478 threads = len(txs) 479 } 480 blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 481 for th := 0; th < threads; th++ { 482 pend.Add(1) 483 go func() { 484 defer pend.Done() 485 // Fetch and execute the next transaction trace tasks 486 for task := range jobs { 487 msg, _ := txs[task.index].AsMessage(signer) 488 res, err := api.traceTx(ctx, msg, blockCtx, task.statedb, config) 489 if err != nil { 490 results[task.index] = &txTraceResult{Error: err.Error()} 491 continue 492 } 493 results[task.index] = &txTraceResult{Result: res} 494 } 495 }() 496 } 497 // Feed the transactions into the tracers and return 498 var failed error 499 for i, tx := range txs { 500 // Send the trace task over for execution 501 jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} 502 503 // Generate the next state snapshot fast without tracing 504 msg, _ := tx.AsMessage(signer) 505 txContext := core.NewEVMTxContext(msg) 506 507 vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{}) 508 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 509 failed = err 510 break 511 } 512 // Finalize the state so any modifications are written to the trie 513 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 514 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 515 } 516 close(jobs) 517 pend.Wait() 518 519 // If execution failed in between, abort 520 if failed != nil { 521 return nil, failed 522 } 523 return results, nil 524 } 525 526 // standardTraceBlockToFile configures a new tracer which uses standard JSON output, 527 // and traces either a full block or an individual transaction. The return value will 528 // be one filename per transaction traced. 529 func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { 530 // If we're tracing a single transaction, make sure it's present 531 if config != nil && config.TxHash != (common.Hash{}) { 532 if !containsTx(block, config.TxHash) { 533 return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash) 534 } 535 } 536 if block.NumberU64() == 0 { 537 return nil, errors.New("genesis is not traceable") 538 } 539 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 540 if err != nil { 541 return nil, err 542 } 543 reexec := defaultTraceReexec 544 if config != nil && config.Reexec != nil { 545 reexec = *config.Reexec 546 } 547 statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec) 548 if err != nil { 549 return nil, err 550 } 551 defer release() 552 553 // Retrieve the tracing configurations, or use default values 554 var ( 555 logConfig vm.LogConfig 556 txHash common.Hash 557 ) 558 if config != nil { 559 logConfig = config.LogConfig 560 txHash = config.TxHash 561 } 562 logConfig.Debug = true 563 564 // Execute transaction, either tracing all or just the requested one 565 var ( 566 dumps []string 567 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) 568 chainConfig = api.backend.ChainConfig() 569 vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 570 canon = true 571 ) 572 // Check if there are any overrides: the caller may wish to enable a future 573 // fork when executing this block. Note, such overrides are only applicable to the 574 // actual specified block, not any preceding blocks that we have to go through 575 // in order to obtain the state. 576 // Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork` 577 578 if config != nil && config.Overrides != nil { 579 // Copy the config, to not screw up the main config 580 // Note: the Clique-part is _not_ deep copied 581 chainConfigCopy := new(params.ChainConfig) 582 *chainConfigCopy = *chainConfig 583 chainConfig = chainConfigCopy 584 if yolov3 := config.LogConfig.Overrides.YoloV3Block; yolov3 != nil { 585 chainConfig.YoloV3Block = yolov3 586 canon = false 587 } 588 } 589 for i, tx := range block.Transactions() { 590 // Prepare the trasaction for un-traced execution 591 var ( 592 msg, _ = tx.AsMessage(signer) 593 txContext = core.NewEVMTxContext(msg) 594 vmConf vm.Config 595 dump *os.File 596 writer *bufio.Writer 597 err error 598 ) 599 // If the transaction needs tracing, swap out the configs 600 if tx.Hash() == txHash || txHash == (common.Hash{}) { 601 // Generate a unique temporary file to dump it into 602 prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) 603 if !canon { 604 prefix = fmt.Sprintf("%valt-", prefix) 605 } 606 dump, err = ioutil.TempFile(os.TempDir(), prefix) 607 if err != nil { 608 return nil, err 609 } 610 dumps = append(dumps, dump.Name()) 611 612 // Swap out the noop logger to the standard tracer 613 writer = bufio.NewWriter(dump) 614 vmConf = vm.Config{ 615 Debug: true, 616 Tracer: vm.NewJSONLogger(&logConfig, writer), 617 EnablePreimageRecording: true, 618 } 619 } 620 // Execute the transaction and flush any traces to disk 621 vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) 622 _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) 623 if writer != nil { 624 writer.Flush() 625 } 626 if dump != nil { 627 dump.Close() 628 log.Info("Wrote standard trace", "file", dump.Name()) 629 } 630 if err != nil { 631 return dumps, err 632 } 633 // Finalize the state so any modifications are written to the trie 634 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 635 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 636 637 // If we've traced the transaction we were looking for, abort 638 if tx.Hash() == txHash { 639 break 640 } 641 } 642 return dumps, nil 643 } 644 645 // containsTx reports whether the transaction with a certain hash 646 // is contained within the specified block. 647 func containsTx(block *types.Block, hash common.Hash) bool { 648 for _, tx := range block.Transactions() { 649 if tx.Hash() == hash { 650 return true 651 } 652 } 653 return false 654 } 655 656 // TraceTransaction returns the structured logs created during the execution of EVM 657 // and returns them as a JSON object. 658 func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { 659 _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) 660 if err != nil { 661 return nil, err 662 } 663 // It shouldn't happen in practice. 664 if blockNumber == 0 { 665 return nil, errors.New("genesis is not traceable") 666 } 667 reexec := defaultTraceReexec 668 if config != nil && config.Reexec != nil { 669 reexec = *config.Reexec 670 } 671 block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) 672 if err != nil { 673 return nil, err 674 } 675 msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) 676 if err != nil { 677 return nil, err 678 } 679 defer release() 680 681 return api.traceTx(ctx, msg, vmctx, statedb, config) 682 } 683 684 // TraceCall lets you trace a given eth_call. It collects the structured logs 685 // created during the execution of EVM if the given transaction was added on 686 // top of the provided block and returns them as a JSON object. 687 // You can provide -2 as a block number to trace on top of the pending block. 688 func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { 689 // Try to retrieve the specified block 690 var ( 691 err error 692 block *types.Block 693 ) 694 if hash, ok := blockNrOrHash.Hash(); ok { 695 block, err = api.blockByHash(ctx, hash) 696 } else if number, ok := blockNrOrHash.Number(); ok { 697 block, err = api.blockByNumber(ctx, number) 698 } 699 if err != nil { 700 return nil, err 701 } 702 // try to recompute the state 703 reexec := defaultTraceReexec 704 if config != nil && config.Reexec != nil { 705 reexec = *config.Reexec 706 } 707 statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec) 708 if err != nil { 709 return nil, err 710 } 711 defer release() 712 713 // Execute the trace 714 msg := args.ToMessage(api.backend.RPCGasCap()) 715 vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 716 return api.traceTx(ctx, msg, vmctx, statedb, config) 717 } 718 719 // traceTx configures a new tracer according to the provided configuration, and 720 // executes the given message in the provided environment. The return value will 721 // be tracer dependent. 722 func (api *API) traceTx(ctx context.Context, message core.Message, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { 723 // Assemble the structured logger or the JavaScript tracer 724 var ( 725 tracer vm.Tracer 726 err error 727 txContext = core.NewEVMTxContext(message) 728 ) 729 switch { 730 case config != nil && config.Tracer != nil: 731 // Define a meaningful timeout of a single transaction trace 732 timeout := defaultTraceTimeout 733 if config.Timeout != nil { 734 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 735 return nil, err 736 } 737 } 738 // Constuct the JavaScript tracer to execute with 739 if tracer, err = New(*config.Tracer, txContext); err != nil { 740 return nil, err 741 } 742 // Handle timeouts and RPC cancellations 743 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 744 go func() { 745 <-deadlineCtx.Done() 746 tracer.(*Tracer).Stop(errors.New("execution timeout")) 747 }() 748 defer cancel() 749 750 case config == nil: 751 tracer = vm.NewStructLogger(nil) 752 753 default: 754 tracer = vm.NewStructLogger(config.LogConfig) 755 } 756 757 // Run the transaction with tracing enabled. 758 vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer}) 759 result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) 760 if err != nil { 761 return nil, fmt.Errorf("tracing failed: %v", err) 762 } 763 764 // Depending on the tracer type, format and return the output. 765 switch tracer := tracer.(type) { 766 case *vm.StructLogger: 767 // If the result contains a revert reason, return it. 768 returnVal := fmt.Sprintf("%x", result.Return()) 769 if len(result.Revert()) > 0 { 770 returnVal = fmt.Sprintf("%x", result.Revert()) 771 } 772 return ðapi.ExecutionResult{ 773 Gas: result.UsedGas, 774 Failed: result.Failed(), 775 ReturnValue: returnVal, 776 StructLogs: ethapi.FormatLogs(tracer.StructLogs()), 777 }, nil 778 779 case *Tracer: 780 return tracer.GetResult() 781 782 default: 783 panic(fmt.Sprintf("bad tracer type %T", tracer)) 784 } 785 } 786 787 // APIs return the collection of RPC services the tracer package offers. 788 func APIs(backend Backend) []rpc.API { 789 // Append all the local APIs and return 790 return []rpc.API{ 791 { 792 Namespace: "debug", 793 Version: "1.0", 794 Service: NewAPI(backend), 795 Public: false, 796 }, 797 } 798 }