gitlab.com/yannislg/go-pulse@v0.0.0-20210722055913-a3e24e95638d/eth/api_tracer.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 eth 18 19 import ( 20 "bufio" 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "github.com/ethereum/go-ethereum/consensus" 26 "io/ioutil" 27 "math/big" 28 "os" 29 "runtime" 30 "sync" 31 "time" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/common/hexutil" 35 "github.com/ethereum/go-ethereum/core" 36 "github.com/ethereum/go-ethereum/core/rawdb" 37 "github.com/ethereum/go-ethereum/core/state" 38 "github.com/ethereum/go-ethereum/core/types" 39 "github.com/ethereum/go-ethereum/core/vm" 40 "github.com/ethereum/go-ethereum/eth/tracers" 41 "github.com/ethereum/go-ethereum/internal/ethapi" 42 "github.com/ethereum/go-ethereum/log" 43 "github.com/ethereum/go-ethereum/rlp" 44 "github.com/ethereum/go-ethereum/rpc" 45 "github.com/ethereum/go-ethereum/trie" 46 ) 47 48 const ( 49 // defaultTraceTimeout is the amount of time a single transaction can execute 50 // by default before being forcefully aborted. 51 defaultTraceTimeout = 5 * time.Second 52 53 // defaultTraceReexec is the number of blocks the tracer is willing to go back 54 // and reexecute to produce missing historical state necessary to run a specific 55 // trace. 56 defaultTraceReexec = uint64(128) 57 ) 58 59 // TraceConfig holds extra parameters to trace functions. 60 type TraceConfig struct { 61 *vm.LogConfig 62 Tracer *string 63 Timeout *string 64 Reexec *uint64 65 } 66 67 // StdTraceConfig holds extra parameters to standard-json trace functions. 68 type StdTraceConfig struct { 69 *vm.LogConfig 70 Reexec *uint64 71 TxHash common.Hash 72 } 73 74 // txTraceResult is the result of a single transaction trace. 75 type txTraceResult struct { 76 Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer 77 Error string `json:"error,omitempty"` // Trace failure produced by the tracer 78 } 79 80 // blockTraceTask represents a single block trace task when an entire chain is 81 // being traced. 82 type blockTraceTask struct { 83 statedb *state.StateDB // Intermediate state prepped for tracing 84 block *types.Block // Block to trace the transactions from 85 rootref common.Hash // Trie root reference held for this task 86 results []*txTraceResult // Trace results procudes by the task 87 } 88 89 // blockTraceResult represets the results of tracing a single block when an entire 90 // chain is being traced. 91 type blockTraceResult struct { 92 Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace 93 Hash common.Hash `json:"hash"` // Block hash corresponding to this trace 94 Traces []*txTraceResult `json:"traces"` // Trace results produced by the task 95 } 96 97 // txTraceTask represents a single transaction trace task when an entire block 98 // is being traced. 99 type txTraceTask struct { 100 statedb *state.StateDB // Intermediate state prepped for tracing 101 index int // Transaction offset in the block 102 } 103 104 // TraceChain returns the structured logs created during the execution of EVM 105 // between two blocks (excluding start) and returns them as a JSON object. 106 func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { 107 // Fetch the block interval that we want to trace 108 var from, to *types.Block 109 110 switch start { 111 case rpc.PendingBlockNumber: 112 from = api.eth.miner.PendingBlock() 113 case rpc.LatestBlockNumber: 114 from = api.eth.blockchain.CurrentBlock() 115 default: 116 from = api.eth.blockchain.GetBlockByNumber(uint64(start)) 117 } 118 switch end { 119 case rpc.PendingBlockNumber: 120 to = api.eth.miner.PendingBlock() 121 case rpc.LatestBlockNumber: 122 to = api.eth.blockchain.CurrentBlock() 123 default: 124 to = api.eth.blockchain.GetBlockByNumber(uint64(end)) 125 } 126 // Trace the chain if we've found all our blocks 127 if from == nil { 128 return nil, fmt.Errorf("starting block #%d not found", start) 129 } 130 if to == nil { 131 return nil, fmt.Errorf("end block #%d not found", end) 132 } 133 if from.Number().Cmp(to.Number()) >= 0 { 134 return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) 135 } 136 return api.traceChain(ctx, from, to, config) 137 } 138 139 // traceChain configures a new tracer according to the provided configuration, and 140 // executes all the transactions contained within. The return value will be one item 141 // per transaction, dependent on the requested tracer. 142 func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { 143 // Tracing a chain is a **long** operation, only do with subscriptions 144 notifier, supported := rpc.NotifierFromContext(ctx) 145 if !supported { 146 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 147 } 148 sub := notifier.CreateSubscription() 149 150 // Ensure we have a valid starting state before doing any work 151 origin := start.NumberU64() 152 database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16) // Chain tracing will probably start at genesis 153 154 if number := start.NumberU64(); number > 0 { 155 start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) 156 if start == nil { 157 return nil, fmt.Errorf("parent block #%d not found", number-1) 158 } 159 } 160 statedb, err := state.New(start.Root(), database, nil) 161 if err != nil { 162 // If the starting state is missing, allow some number of blocks to be reexecuted 163 reexec := defaultTraceReexec 164 if config != nil && config.Reexec != nil { 165 reexec = *config.Reexec 166 } 167 // Find the most recent block that has the state available 168 for i := uint64(0); i < reexec; i++ { 169 start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) 170 if start == nil { 171 break 172 } 173 if statedb, err = state.New(start.Root(), database, nil); err == nil { 174 break 175 } 176 } 177 // If we still don't have the state available, bail out 178 if err != nil { 179 switch err.(type) { 180 case *trie.MissingNodeError: 181 return nil, errors.New("required historical state unavailable") 182 default: 183 return nil, err 184 } 185 } 186 } 187 // Execute all the transaction contained within the chain concurrently for each block 188 blocks := int(end.NumberU64() - origin) 189 190 threads := runtime.NumCPU() 191 if threads > blocks { 192 threads = blocks 193 } 194 var ( 195 pend = new(sync.WaitGroup) 196 tasks = make(chan *blockTraceTask, threads) 197 results = make(chan *blockTraceTask, threads) 198 ) 199 for th := 0; th < threads; th++ { 200 pend.Add(1) 201 go func() { 202 defer pend.Done() 203 204 // Fetch and execute the next block trace tasks 205 for task := range tasks { 206 signer := types.MakeSigner(api.eth.blockchain.Config(), task.block.Number()) 207 208 // Trace all the transactions contained within 209 for i, tx := range task.block.Transactions() { 210 msg, _ := tx.AsMessage(signer) 211 vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) 212 213 res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config) 214 if err != nil { 215 task.results[i] = &txTraceResult{Error: err.Error()} 216 log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) 217 break 218 } 219 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 220 task.statedb.Finalise(api.eth.blockchain.Config().IsEIP158(task.block.Number())) 221 task.results[i] = &txTraceResult{Result: res} 222 } 223 // Stream the result back to the user or abort on teardown 224 select { 225 case results <- task: 226 case <-notifier.Closed(): 227 return 228 } 229 } 230 }() 231 } 232 // Start a goroutine to feed all the blocks into the tracers 233 begin := time.Now() 234 235 go func() { 236 var ( 237 logged time.Time 238 number uint64 239 traced uint64 240 failed error 241 proot common.Hash 242 ) 243 // Ensure everything is properly cleaned up on any exit path 244 defer func() { 245 close(tasks) 246 pend.Wait() 247 248 switch { 249 case failed != nil: 250 log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed) 251 case number < end.NumberU64(): 252 log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin)) 253 default: 254 log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) 255 } 256 close(results) 257 }() 258 // Feed all the blocks both into the tracer, as well as fast process concurrently 259 for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ { 260 // Stop tracing if interruption was requested 261 select { 262 case <-notifier.Closed(): 263 return 264 default: 265 } 266 // Print progress logs if long enough time elapsed 267 if time.Since(logged) > 8*time.Second { 268 if number > origin { 269 nodes, imgs := database.TrieDB().Size() 270 log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin), "memory", nodes+imgs) 271 } else { 272 log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin)) 273 } 274 logged = time.Now() 275 } 276 // Retrieve the next block to trace 277 block := api.eth.blockchain.GetBlockByNumber(number) 278 if block == nil { 279 failed = fmt.Errorf("block #%d not found", number) 280 break 281 } 282 // Send the block over to the concurrent tracers (if not in the fast-forward phase) 283 if number > origin { 284 txs := block.Transactions() 285 286 select { 287 case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, rootref: proot, results: make([]*txTraceResult, len(txs))}: 288 case <-notifier.Closed(): 289 return 290 } 291 traced += uint64(len(txs)) 292 } 293 // Generate the next state snapshot fast without tracing 294 _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{}) 295 if err != nil { 296 failed = err 297 break 298 } 299 // Finalize the state so any modifications are written to the trie 300 root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number())) 301 if err != nil { 302 failed = err 303 break 304 } 305 if err := statedb.Reset(root); err != nil { 306 failed = err 307 break 308 } 309 // Reference the trie twice, once for us, once for the tracer 310 database.TrieDB().Reference(root, common.Hash{}) 311 if number >= origin { 312 database.TrieDB().Reference(root, common.Hash{}) 313 } 314 // Dereference all past tries we ourselves are done working with 315 if proot != (common.Hash{}) { 316 database.TrieDB().Dereference(proot) 317 } 318 proot = root 319 320 // TODO(karalabe): Do we need the preimages? Won't they accumulate too much? 321 } 322 }() 323 324 // Keep reading the trace results and stream the to the user 325 go func() { 326 var ( 327 done = make(map[uint64]*blockTraceResult) 328 next = origin + 1 329 ) 330 for res := range results { 331 // Queue up next received result 332 result := &blockTraceResult{ 333 Block: hexutil.Uint64(res.block.NumberU64()), 334 Hash: res.block.Hash(), 335 Traces: res.results, 336 } 337 done[uint64(result.Block)] = result 338 339 // Dereference any paret tries held in memory by this task 340 database.TrieDB().Dereference(res.rootref) 341 342 // Stream completed traces to the user, aborting on the first error 343 for result, ok := done[next]; ok; result, ok = done[next] { 344 if len(result.Traces) > 0 || next == end.NumberU64() { 345 notifier.Notify(sub.ID, result) 346 } 347 delete(done, next) 348 next++ 349 } 350 } 351 }() 352 return sub, nil 353 } 354 355 // TraceBlockByNumber returns the structured logs created during the execution of 356 // EVM and returns them as a JSON object. 357 func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { 358 // Fetch the block that we want to trace 359 var block *types.Block 360 361 switch number { 362 case rpc.PendingBlockNumber: 363 block = api.eth.miner.PendingBlock() 364 case rpc.LatestBlockNumber: 365 block = api.eth.blockchain.CurrentBlock() 366 default: 367 block = api.eth.blockchain.GetBlockByNumber(uint64(number)) 368 } 369 // Trace the block if it was found 370 if block == nil { 371 return nil, fmt.Errorf("block #%d not found", number) 372 } 373 return api.traceBlock(ctx, block, config) 374 } 375 376 // TraceBlockByHash returns the structured logs created during the execution of 377 // EVM and returns them as a JSON object. 378 func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 379 block := api.eth.blockchain.GetBlockByHash(hash) 380 if block == nil { 381 return nil, fmt.Errorf("block %#x not found", hash) 382 } 383 return api.traceBlock(ctx, block, config) 384 } 385 386 // TraceBlock returns the structured logs created during the execution of EVM 387 // and returns them as a JSON object. 388 func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { 389 block := new(types.Block) 390 if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { 391 return nil, fmt.Errorf("could not decode block: %v", err) 392 } 393 return api.traceBlock(ctx, block, config) 394 } 395 396 // TraceBlockFromFile returns the structured logs created during the execution of 397 // EVM and returns them as a JSON object. 398 func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { 399 blob, err := ioutil.ReadFile(file) 400 if err != nil { 401 return nil, fmt.Errorf("could not read file: %v", err) 402 } 403 return api.TraceBlock(ctx, blob, config) 404 } 405 406 // TraceBadBlockByHash returns the structured logs created during the execution of 407 // EVM against a block pulled from the pool of bad ones and returns them as a JSON 408 // object. 409 func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 410 blocks := api.eth.blockchain.BadBlocks() 411 for _, block := range blocks { 412 if block.Hash() == hash { 413 return api.traceBlock(ctx, block, config) 414 } 415 } 416 return nil, fmt.Errorf("bad block %#x not found", hash) 417 } 418 419 // StandardTraceBlockToFile dumps the structured logs created during the 420 // execution of EVM to the local file system and returns a list of files 421 // to the caller. 422 func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 423 block := api.eth.blockchain.GetBlockByHash(hash) 424 if block == nil { 425 return nil, fmt.Errorf("block %#x not found", hash) 426 } 427 return api.standardTraceBlockToFile(ctx, block, config) 428 } 429 430 // StandardTraceBadBlockToFile dumps the structured logs created during the 431 // execution of EVM against a block pulled from the pool of bad ones to the 432 // local file system and returns a list of files to the caller. 433 func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 434 blocks := api.eth.blockchain.BadBlocks() 435 for _, block := range blocks { 436 if block.Hash() == hash { 437 return api.standardTraceBlockToFile(ctx, block, config) 438 } 439 } 440 return nil, fmt.Errorf("bad block %#x not found", hash) 441 } 442 443 // traceBlock configures a new tracer according to the provided configuration, and 444 // executes all the transactions contained within. The return value will be one item 445 // per transaction, dependent on the requestd tracer. 446 func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { 447 // Create the parent state database 448 if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { 449 return nil, err 450 } 451 parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 452 if parent == nil { 453 return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) 454 } 455 reexec := defaultTraceReexec 456 if config != nil && config.Reexec != nil { 457 reexec = *config.Reexec 458 } 459 statedb, err := api.computeStateDB(parent, reexec) 460 if err != nil { 461 return nil, err 462 } 463 // Execute all the transaction contained within the block concurrently 464 var ( 465 signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number()) 466 467 txs = block.Transactions() 468 results = make([]*txTraceResult, len(txs)) 469 470 pend = new(sync.WaitGroup) 471 jobs = make(chan *txTraceTask, len(txs)) 472 ) 473 threads := runtime.NumCPU() 474 if threads > len(txs) { 475 threads = len(txs) 476 } 477 for th := 0; th < threads; th++ { 478 pend.Add(1) 479 go func() { 480 defer pend.Done() 481 482 // Fetch and execute the next transaction trace tasks 483 for task := range jobs { 484 msg, _ := txs[task.index].AsMessage(signer) 485 vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 486 487 res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config) 488 if err != nil { 489 results[task.index] = &txTraceResult{Error: err.Error()} 490 continue 491 } 492 results[task.index] = &txTraceResult{Result: res} 493 } 494 }() 495 } 496 // Feed the transactions into the tracers and return 497 var failed error 498 for i, tx := range txs { 499 // Send the trace task over for execution 500 jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} 501 502 // Generate the next state snapshot fast without tracing 503 msg, _ := tx.AsMessage(signer) 504 vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 505 if posa, ok := api.eth.engine.(consensus.PoSA); ok { 506 if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { 507 balance := statedb.GetBalance(consensus.SystemAddress) 508 if balance.Cmp(common.Big0) > 0 { 509 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 510 statedb.AddBalance(block.Header().Coinbase, balance) 511 } 512 } 513 } 514 vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{}) 515 if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 516 failed = err 517 break 518 } 519 // Finalize the state so any modifications are written to the trie 520 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 521 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 522 } 523 close(jobs) 524 pend.Wait() 525 526 // If execution failed in between, abort 527 if failed != nil { 528 return nil, failed 529 } 530 return results, nil 531 } 532 533 // standardTraceBlockToFile configures a new tracer which uses standard JSON output, 534 // and traces either a full block or an individual transaction. The return value will 535 // be one filename per transaction traced. 536 func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { 537 // If we're tracing a single transaction, make sure it's present 538 if config != nil && config.TxHash != (common.Hash{}) { 539 if !containsTx(block, config.TxHash) { 540 return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash) 541 } 542 } 543 // Create the parent state database 544 if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { 545 return nil, err 546 } 547 parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 548 if parent == nil { 549 return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) 550 } 551 reexec := defaultTraceReexec 552 if config != nil && config.Reexec != nil { 553 reexec = *config.Reexec 554 } 555 statedb, err := api.computeStateDB(parent, reexec) 556 if err != nil { 557 return nil, err 558 } 559 // Retrieve the tracing configurations, or use default values 560 var ( 561 logConfig vm.LogConfig 562 txHash common.Hash 563 ) 564 if config != nil { 565 if config.LogConfig != nil { 566 logConfig = *config.LogConfig 567 } 568 txHash = config.TxHash 569 } 570 logConfig.Debug = true 571 572 // Execute transaction, either tracing all or just the requested one 573 var ( 574 signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number()) 575 dumps []string 576 ) 577 for i, tx := range block.Transactions() { 578 // Prepare the trasaction for un-traced execution 579 var ( 580 msg, _ = tx.AsMessage(signer) 581 vmctx = core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 582 583 vmConf vm.Config 584 dump *os.File 585 writer *bufio.Writer 586 err error 587 ) 588 // If the transaction needs tracing, swap out the configs 589 if tx.Hash() == txHash || txHash == (common.Hash{}) { 590 // Generate a unique temporary file to dump it into 591 prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) 592 593 dump, err = ioutil.TempFile(os.TempDir(), prefix) 594 if err != nil { 595 return nil, err 596 } 597 dumps = append(dumps, dump.Name()) 598 599 // Swap out the noop logger to the standard tracer 600 writer = bufio.NewWriter(dump) 601 vmConf = vm.Config{ 602 Debug: true, 603 Tracer: vm.NewJSONLogger(&logConfig, writer), 604 EnablePreimageRecording: true, 605 } 606 } 607 // Execute the transaction and flush any traces to disk 608 vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf) 609 _, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) 610 if writer != nil { 611 writer.Flush() 612 } 613 if dump != nil { 614 dump.Close() 615 log.Info("Wrote standard trace", "file", dump.Name()) 616 } 617 if err != nil { 618 return dumps, err 619 } 620 // Finalize the state so any modifications are written to the trie 621 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 622 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 623 624 // If we've traced the transaction we were looking for, abort 625 if tx.Hash() == txHash { 626 break 627 } 628 } 629 return dumps, nil 630 } 631 632 // containsTx reports whether the transaction with a certain hash 633 // is contained within the specified block. 634 func containsTx(block *types.Block, hash common.Hash) bool { 635 for _, tx := range block.Transactions() { 636 if tx.Hash() == hash { 637 return true 638 } 639 } 640 return false 641 } 642 643 // computeStateDB retrieves the state database associated with a certain block. 644 // If no state is locally available for the given block, a number of blocks are 645 // attempted to be reexecuted to generate the desired state. 646 func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) { 647 // If we have the state fully available, use that 648 statedb, err := api.eth.blockchain.StateAt(block.Root()) 649 if err == nil { 650 return statedb, nil 651 } 652 // Otherwise try to reexec blocks until we find a state or reach our limit 653 origin := block.NumberU64() 654 database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16) 655 656 for i := uint64(0); i < reexec; i++ { 657 block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 658 if block == nil { 659 break 660 } 661 if statedb, err = state.New(block.Root(), database, nil); err == nil { 662 break 663 } 664 } 665 if err != nil { 666 switch err.(type) { 667 case *trie.MissingNodeError: 668 return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec) 669 default: 670 return nil, err 671 } 672 } 673 // State was available at historical point, regenerate 674 var ( 675 start = time.Now() 676 logged time.Time 677 proot common.Hash 678 ) 679 for block.NumberU64() < origin { 680 // Print progress logs if long enough time elapsed 681 if time.Since(logged) > 8*time.Second { 682 log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start)) 683 logged = time.Now() 684 } 685 // Retrieve the next block to regenerate and process it 686 if block = api.eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil { 687 return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1) 688 } 689 _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{}) 690 if err != nil { 691 return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err) 692 } 693 // Finalize the state so any modifications are written to the trie 694 root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number())) 695 if err != nil { 696 return nil, err 697 } 698 if err := statedb.Reset(root); err != nil { 699 return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err) 700 } 701 database.TrieDB().Reference(root, common.Hash{}) 702 if proot != (common.Hash{}) { 703 database.TrieDB().Dereference(proot) 704 } 705 proot = root 706 } 707 nodes, imgs := database.TrieDB().Size() 708 log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) 709 return statedb, nil 710 } 711 712 // TraceTransaction returns the structured logs created during the execution of EVM 713 // and returns them as a JSON object. 714 func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { 715 // Retrieve the transaction and assemble its EVM context 716 tx, blockHash, _, index := rawdb.ReadTransaction(api.eth.ChainDb(), hash) 717 if tx == nil { 718 return nil, fmt.Errorf("transaction %#x not found", hash) 719 } 720 reexec := defaultTraceReexec 721 if config != nil && config.Reexec != nil { 722 reexec = *config.Reexec 723 } 724 msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), reexec) 725 if err != nil { 726 return nil, err 727 } 728 // Trace the transaction and return 729 return api.traceTx(ctx, msg, vmctx, statedb, config) 730 } 731 732 // traceTx configures a new tracer according to the provided configuration, and 733 // executes the given message in the provided environment. The return value will 734 // be tracer dependent. 735 func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { 736 // Assemble the structured logger or the JavaScript tracer 737 var ( 738 tracer vm.Tracer 739 err error 740 ) 741 switch { 742 case config != nil && config.Tracer != nil: 743 // Define a meaningful timeout of a single transaction trace 744 timeout := defaultTraceTimeout 745 if config.Timeout != nil { 746 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 747 return nil, err 748 } 749 } 750 // Constuct the JavaScript tracer to execute with 751 if tracer, err = tracers.New(*config.Tracer); err != nil { 752 return nil, err 753 } 754 // Handle timeouts and RPC cancellations 755 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 756 go func() { 757 <-deadlineCtx.Done() 758 tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) 759 }() 760 defer cancel() 761 762 case config == nil: 763 tracer = vm.NewStructLogger(nil) 764 765 default: 766 tracer = vm.NewStructLogger(config.LogConfig) 767 } 768 // Run the transaction with tracing enabled. 769 vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer}) 770 if posa, ok := api.eth.engine.(consensus.PoSA); ok && message.From() == vmctx.Coinbase && 771 posa.IsSystemContract(message.To()) && message.GasPrice().Cmp(big.NewInt(0)) == 0 { 772 balance := statedb.GetBalance(consensus.SystemAddress) 773 if balance.Cmp(common.Big0) > 0 { 774 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 775 statedb.AddBalance(vmctx.Coinbase, balance) 776 } 777 } 778 ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) 779 if err != nil { 780 return nil, fmt.Errorf("tracing failed: %v", err) 781 } 782 // Depending on the tracer type, format and return the output 783 switch tracer := tracer.(type) { 784 case *vm.StructLogger: 785 return ðapi.ExecutionResult{ 786 Gas: gas, 787 Failed: failed, 788 ReturnValue: fmt.Sprintf("%x", ret), 789 StructLogs: ethapi.FormatLogs(tracer.StructLogs()), 790 }, nil 791 792 case *tracers.Tracer: 793 return tracer.GetResult() 794 795 default: 796 panic(fmt.Sprintf("bad tracer type %T", tracer)) 797 } 798 } 799 800 // computeTxEnv returns the execution environment of a certain transaction. 801 func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { 802 // Create the parent state database 803 block := api.eth.blockchain.GetBlockByHash(blockHash) 804 if block == nil { 805 return nil, vm.Context{}, nil, fmt.Errorf("block %#x not found", blockHash) 806 } 807 parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 808 if parent == nil { 809 return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) 810 } 811 statedb, err := api.computeStateDB(parent, reexec) 812 if err != nil { 813 return nil, vm.Context{}, nil, err 814 } 815 816 if txIndex == 0 && len(block.Transactions()) == 0 { 817 return nil, vm.Context{}, statedb, nil 818 } 819 820 // Recompute transactions up to the target index. 821 signer := types.MakeSigner(api.eth.blockchain.Config(), block.Number()) 822 823 for idx, tx := range block.Transactions() { 824 // Assemble the transaction call message and return if the requested offset 825 msg, _ := tx.AsMessage(signer) 826 if posa, ok := api.eth.engine.(consensus.PoSA); ok { 827 if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { 828 balance := statedb.GetBalance(consensus.SystemAddress) 829 if balance.Cmp(common.Big0) > 0 { 830 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 831 statedb.AddBalance(block.Header().Coinbase, balance) 832 } 833 } 834 } 835 context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 836 if idx == txIndex { 837 return msg, context, statedb, nil 838 } 839 // Not yet the searched for transaction, execute on top of the current state 840 vmenv := vm.NewEVM(context, statedb, api.eth.blockchain.Config(), vm.Config{}) 841 if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { 842 return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) 843 } 844 // Ensure any modifications are committed to the state 845 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 846 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 847 } 848 return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash) 849 }