17
17
package eth
18
18
19
19
import (
20
+ "bufio"
20
21
"bytes"
21
22
"context"
22
23
"errors"
23
24
"fmt"
24
25
"io/ioutil"
26
+ "os"
25
27
"runtime"
26
28
"sync"
27
29
"time"
@@ -60,6 +62,13 @@ type TraceConfig struct {
60
62
Reexec * uint64
61
63
}
62
64
65
+ // StdTraceConfig holds extra parameters to standard-json trace functions.
66
+ type StdTraceConfig struct {
67
+ * vm.LogConfig
68
+ Reexec * uint64
69
+ TxHash * common.Hash
70
+ }
71
+
63
72
// txTraceResult is the result of a single transaction trace.
64
73
type txTraceResult struct {
65
74
Result interface {} `json:"result,omitempty"` // Trace results produced by the tracer
@@ -391,13 +400,37 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string,
391
400
return api .TraceBlock (ctx , blob , config )
392
401
}
393
402
394
- // TraceBadBlock returns the structured logs created during the execution of a block
395
- // within the blockchain 'badblocks' cache
396
- func (api * PrivateDebugAPI ) TraceBadBlock (ctx context.Context , index int , config * TraceConfig ) ([]* txTraceResult , error ) {
397
- if blocks := api .eth .blockchain .BadBlocks (); index < len (blocks ) {
398
- return api .traceBlock (ctx , blocks [index ], config )
403
+ // TraceBadBlockByHash returns the structured logs created during the execution of a block
404
+ func (api * PrivateDebugAPI ) TraceBadBlock (ctx context.Context , blockHash common.Hash , config * TraceConfig ) ([]* txTraceResult , error ) {
405
+ blocks := api .eth .blockchain .BadBlocks ()
406
+ for _ , block := range blocks {
407
+ if block .Hash () == blockHash {
408
+ return api .traceBlock (ctx , block , config )
409
+ }
410
+ }
411
+ return nil , fmt .Errorf ("hash not found among bad blocks" )
412
+ }
413
+
414
+ // StandardTraceBadBlockToFile dumps the standard-json logs to files on the local filesystem,
415
+ // and returns a list of files to the caller.
416
+ func (api * PrivateDebugAPI ) StandardTraceBadBlockToFile (ctx context.Context , blockHash common.Hash , stdConfig * StdTraceConfig ) ([]string , error ) {
417
+ blocks := api .eth .blockchain .BadBlocks ()
418
+ for _ , block := range blocks {
419
+ if block .Hash () == blockHash {
420
+ return api .standardTraceBlockToFile (ctx , block , stdConfig )
421
+ }
399
422
}
400
- return nil , fmt .Errorf ("index out of range" )
423
+ return nil , fmt .Errorf ("hash not found among bad blocks" )
424
+ }
425
+
426
+ // StandardTraceBlockToFile dumps the standard-json logs to files on the local filesystem,
427
+ // and returns a list of files to the caller.
428
+ func (api * PrivateDebugAPI ) StandardTraceBlockToFile (ctx context.Context , blockHash common.Hash , stdConfig * StdTraceConfig ) ([]string , error ) {
429
+ block := api .eth .blockchain .GetBlockByHash (blockHash )
430
+ if block == nil {
431
+ return nil , fmt .Errorf ("block #%x not found" , blockHash )
432
+ }
433
+ return api .standardTraceBlockToFile (ctx , block , stdConfig )
401
434
}
402
435
403
436
// traceBlock configures a new tracer according to the provided configuration, and
@@ -481,6 +514,92 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
481
514
return results , nil
482
515
}
483
516
517
+ // standardTraceBlockToFile configures a new tracer which uses standard-json output, and
518
+ // traces either a full block or an individual transaction. The return value will be one filename
519
+ // per transaction traced.
520
+ func (api * PrivateDebugAPI ) standardTraceBlockToFile (ctx context.Context , block * types.Block , stdConfig * StdTraceConfig ) ([]string , error ) {
521
+ // Create the parent state database
522
+ if err := api .eth .engine .VerifyHeader (api .eth .blockchain , block .Header (), true ); err != nil {
523
+ return nil , err
524
+ }
525
+ parent := api .eth .blockchain .GetBlock (block .ParentHash (), block .NumberU64 ()- 1 )
526
+ if parent == nil {
527
+ return nil , fmt .Errorf ("parent %x not found" , block .ParentHash ())
528
+ }
529
+ var (
530
+ signer = types .MakeSigner (api .config , block .Number ())
531
+ done = false
532
+ blockPrefix = fmt .Sprintf ("block_0x%x" , block .Hash ().Bytes ()[:4 ])
533
+ usedLogConfig = & vm.LogConfig {Debug : true }
534
+ files []string
535
+ reExec_val = defaultTraceReexec
536
+ txHash * common.Hash
537
+ )
538
+ if stdConfig != nil {
539
+ if stdConfig .Reexec != nil {
540
+ reExec_val = * stdConfig .Reexec
541
+ }
542
+ if stdConfig .LogConfig != nil {
543
+ usedLogConfig .DisableMemory = stdConfig .LogConfig .DisableMemory
544
+ usedLogConfig .DisableStack = stdConfig .LogConfig .DisableStack
545
+ usedLogConfig .DisableStorage = stdConfig .LogConfig .DisableStorage
546
+ usedLogConfig .Limit = stdConfig .LogConfig .Limit
547
+ }
548
+ txHash = stdConfig .TxHash
549
+ }
550
+ statedb , err := api .computeStateDB (parent , reExec_val )
551
+ if err != nil {
552
+ return nil , err
553
+ }
554
+
555
+ for i , tx := range block .Transactions () {
556
+ var (
557
+ outfile * os.File
558
+ err error
559
+ )
560
+ msg , _ := tx .AsMessage (signer )
561
+ vmctx := core .NewEVMContext (msg , block .Header (), api .eth .blockchain , nil )
562
+ vmConf := vm.Config {}
563
+ if txHash == nil || bytes .Equal (txHash .Bytes (), tx .Hash ().Bytes ()) {
564
+ prefix := fmt .Sprintf ("%v-%d-0x%x-" , blockPrefix , i , tx .Hash ().Bytes ()[:4 ])
565
+ // Open a file to dump trace into
566
+ outfile , err = ioutil .TempFile (os .TempDir (), prefix )
567
+ if err != nil {
568
+ return nil , err
569
+ }
570
+ files = append (files , outfile .Name ())
571
+ vmConf = vm.Config {
572
+ Debug : true ,
573
+ Tracer : vm .NewJSONLogger (usedLogConfig , bufio .NewWriter (outfile )),
574
+ EnablePreimageRecording : true ,
575
+ }
576
+ if txHash != nil { // Only one tx to trace
577
+ done = true
578
+ }
579
+ }
580
+ vmenv := vm .NewEVM (vmctx , statedb , api .config , vmConf )
581
+ _ , _ , _ , err = core .ApplyMessage (vmenv , msg , new (core.GasPool ).AddGas (msg .Gas ()))
582
+
583
+ if outfile != nil {
584
+ outfile .Close ()
585
+ log .Info ("Wrote trace" , "file" , outfile .Name ())
586
+ }
587
+ if err != nil {
588
+ return files , err
589
+ }
590
+ // Finalize the state so any modifications are written to the trie
591
+ statedb .Finalise (true )
592
+
593
+ if done {
594
+ break
595
+ }
596
+ }
597
+ if txHash != nil && ! done {
598
+ return nil , fmt .Errorf ("transaction hash not found in block" )
599
+ }
600
+ return files , nil
601
+ }
602
+
484
603
// computeStateDB retrieves the state database associated with a certain block.
485
604
// If no state is locally available for the given block, a number of blocks are
486
605
// attempted to be reexecuted to generate the desired state.
@@ -506,7 +625,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
506
625
if err != nil {
507
626
switch err .(type ) {
508
627
case * trie.MissingNodeError :
509
- return nil , errors . New ("required historical state unavailable" )
628
+ return nil , fmt . Errorf ("required historical state unavailable (reexec=%d)" , reexec )
510
629
default :
511
630
return nil , err
512
631
}
@@ -520,7 +639,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
520
639
for block .NumberU64 () < origin {
521
640
// Print progress logs if long enough time elapsed
522
641
if time .Since (logged ) > 8 * time .Second {
523
- log .Info ("Regenerating historical state" , "block" , block .NumberU64 ()+ 1 , "target" , origin , "elapsed" , time .Since (start ))
642
+ log .Info ("Regenerating historical state" , "block" , block .NumberU64 ()+ 1 , "target" , origin , "remaining" , origin - block . NumberU64 () - 1 , " elapsed" , time .Since (start ))
524
643
logged = time .Now ()
525
644
}
526
645
// Retrieve the next block to regenerate and process it
@@ -529,15 +648,15 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
529
648
}
530
649
_ , _ , _ , err := api .eth .blockchain .Processor ().Process (block , statedb , vm.Config {})
531
650
if err != nil {
532
- return nil , err
651
+ return nil , fmt . Errorf ( "processing block %d failed: %v" , block . NumberU64 (), err )
533
652
}
534
653
// Finalize the state so any modifications are written to the trie
535
- root , err := statedb .Commit (true )
654
+ root , err := statedb .Commit (api . eth . blockchain . Config (). IsEIP158 ( block . Number ()) )
536
655
if err != nil {
537
656
return nil , err
538
657
}
539
658
if err := statedb .Reset (root ); err != nil {
540
- return nil , err
659
+ return nil , fmt . Errorf ( "state reset after block %d failed: %v" , block . NumberU64 (), err )
541
660
}
542
661
database .TrieDB ().Reference (root , common.Hash {})
543
662
if proot != (common.Hash {}) {
0 commit comments