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