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