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
@@ -366,7 +375,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B
366
375
func (api * PrivateDebugAPI ) TraceBlockByHash (ctx context.Context , hash common.Hash , config * TraceConfig ) ([]* txTraceResult , error ) {
367
376
block := api .eth .blockchain .GetBlockByHash (hash )
368
377
if block == nil {
369
- return nil , fmt .Errorf ("block #% x not found" , hash )
378
+ return nil , fmt .Errorf ("block %# x not found" , hash )
370
379
}
371
380
return api .traceBlock (ctx , block , config )
372
381
}
@@ -391,13 +400,41 @@ 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
404
+ // EVM against a block pulled from the pool of bad ones and returns them as a JSON
405
+ // object.
406
+ func (api * PrivateDebugAPI ) TraceBadBlock (ctx context.Context , hash common.Hash , config * TraceConfig ) ([]* txTraceResult , error ) {
407
+ blocks := api .eth .blockchain .BadBlocks ()
408
+ for _ , block := range blocks {
409
+ if block .Hash () == hash {
410
+ return api .traceBlock (ctx , block , config )
411
+ }
412
+ }
413
+ return nil , fmt .Errorf ("bad block %#x not found" , hash )
414
+ }
415
+
416
+ // StandardTraceBlockToFile dumps the structured logs created during the
417
+ // execution of EVM to the local file system and returns a list of files
418
+ // to the caller.
419
+ func (api * PrivateDebugAPI ) StandardTraceBlockToFile (ctx context.Context , hash common.Hash , config * StdTraceConfig ) ([]string , error ) {
420
+ block := api .eth .blockchain .GetBlockByHash (hash )
421
+ if block == nil {
422
+ return nil , fmt .Errorf ("block %#x not found" , hash )
399
423
}
400
- return nil , fmt .Errorf ("index out of range" )
424
+ return api .standardTraceBlockToFile (ctx , block , config )
425
+ }
426
+
427
+ // StandardTraceBadBlockToFile dumps the structured logs created during the
428
+ // execution of EVM against a block pulled from the pool of bad ones to the
429
+ // local file system and returns a list of files to the caller.
430
+ func (api * PrivateDebugAPI ) StandardTraceBadBlockToFile (ctx context.Context , hash common.Hash , config * StdTraceConfig ) ([]string , error ) {
431
+ blocks := api .eth .blockchain .BadBlocks ()
432
+ for _ , block := range blocks {
433
+ if block .Hash () == hash {
434
+ return api .standardTraceBlockToFile (ctx , block , config )
435
+ }
436
+ }
437
+ return nil , fmt .Errorf ("bad block %#x not found" , hash )
401
438
}
402
439
403
440
// traceBlock configures a new tracer according to the provided configuration, and
@@ -410,7 +447,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
410
447
}
411
448
parent := api .eth .blockchain .GetBlock (block .ParentHash (), block .NumberU64 ()- 1 )
412
449
if parent == nil {
413
- return nil , fmt .Errorf ("parent %x not found" , block .ParentHash ())
450
+ return nil , fmt .Errorf ("parent %# x not found" , block .ParentHash ())
414
451
}
415
452
reexec := defaultTraceReexec
416
453
if config != nil && config .Reexec != nil {
@@ -481,6 +518,106 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
481
518
return results , nil
482
519
}
483
520
521
+ // standardTraceBlockToFile configures a new tracer which uses standard JSON output,
522
+ // and traces either a full block or an individual transaction. The return value will
523
+ // be one filename per transaction traced.
524
+ func (api * PrivateDebugAPI ) standardTraceBlockToFile (ctx context.Context , block * types.Block , config * StdTraceConfig ) ([]string , error ) {
525
+ // If we're tracing a single transaction, make sure it's present
526
+ if config != nil && config .TxHash != (common.Hash {}) {
527
+ var exists bool
528
+ for _ , tx := range block .Transactions () {
529
+ if exists = (tx .Hash () == config .TxHash ); exists {
530
+ break
531
+ }
532
+ }
533
+ if ! exists {
534
+ return nil , fmt .Errorf ("transaction %#x not found in block" , config .TxHash )
535
+ }
536
+ }
537
+ // Create the parent state database
538
+ if err := api .eth .engine .VerifyHeader (api .eth .blockchain , block .Header (), true ); err != nil {
539
+ return nil , err
540
+ }
541
+ parent := api .eth .blockchain .GetBlock (block .ParentHash (), block .NumberU64 ()- 1 )
542
+ if parent == nil {
543
+ return nil , fmt .Errorf ("parent %#x not found" , block .ParentHash ())
544
+ }
545
+ reexec := defaultTraceReexec
546
+ if config != nil && config .Reexec != nil {
547
+ reexec = * config .Reexec
548
+ }
549
+ statedb , err := api .computeStateDB (parent , reexec )
550
+ if err != nil {
551
+ return nil , err
552
+ }
553
+ // Retrieve the tracing configurations, or use default values
554
+ var (
555
+ logConfig vm.LogConfig
556
+ txHash common.Hash
557
+ )
558
+ if config != nil {
559
+ if config .LogConfig != nil {
560
+ logConfig = * config .LogConfig
561
+ }
562
+ txHash = config .TxHash
563
+ }
564
+ logConfig .Debug = true
565
+
566
+ // Execute transaction, either tracing all or just the requested one
567
+ var (
568
+ signer = types .MakeSigner (api .config , block .Number ())
569
+ dumps []string
570
+ )
571
+ for i , tx := range block .Transactions () {
572
+ // Prepare the trasaction for un-traced execution
573
+ var (
574
+ msg , _ = tx .AsMessage (signer )
575
+ vmctx = core .NewEVMContext (msg , block .Header (), api .eth .blockchain , nil )
576
+
577
+ vmConf vm.Config
578
+ dump * os.File
579
+ err error
580
+ )
581
+ // If the transaction needs tracing, swap out the configs
582
+ if tx .Hash () == txHash || txHash == (common.Hash {}) {
583
+ // Generate a unique temporary file to dump it into
584
+ prefix := fmt .Sprintf ("block_%#x-%d-%#x-" , block .Hash ().Bytes ()[:4 ], i , tx .Hash ().Bytes ()[:4 ])
585
+
586
+ dump , err = ioutil .TempFile (os .TempDir (), prefix )
587
+ if err != nil {
588
+ return nil , err
589
+ }
590
+ dumps = append (dumps , dump .Name ())
591
+
592
+ // Swap out the noop logger to the standard tracer
593
+ vmConf = vm.Config {
594
+ Debug : true ,
595
+ Tracer : vm .NewJSONLogger (& logConfig , bufio .NewWriter (dump )),
596
+ EnablePreimageRecording : true ,
597
+ }
598
+ }
599
+ // Execute the transaction and flush any traces to disk
600
+ vmenv := vm .NewEVM (vmctx , statedb , api .config , vmConf )
601
+ _ , _ , _ , err = core .ApplyMessage (vmenv , msg , new (core.GasPool ).AddGas (msg .Gas ()))
602
+
603
+ if dump != nil {
604
+ dump .Close ()
605
+ log .Info ("Wrote standard trace" , "file" , dump .Name ())
606
+ }
607
+ if err != nil {
608
+ return dumps , err
609
+ }
610
+ // Finalize the state so any modifications are written to the trie
611
+ statedb .Finalise (true )
612
+
613
+ // If we've traced the transaction we were looking for, abort
614
+ if tx .Hash () == txHash {
615
+ break
616
+ }
617
+ }
618
+ return dumps , nil
619
+ }
620
+
484
621
// computeStateDB retrieves the state database associated with a certain block.
485
622
// If no state is locally available for the given block, a number of blocks are
486
623
// attempted to be reexecuted to generate the desired state.
@@ -506,7 +643,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
506
643
if err != nil {
507
644
switch err .(type ) {
508
645
case * trie.MissingNodeError :
509
- return nil , errors . New ("required historical state unavailable" )
646
+ return nil , fmt . Errorf ("required historical state unavailable (reexec=%d)" , reexec )
510
647
default :
511
648
return nil , err
512
649
}
@@ -520,7 +657,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
520
657
for block .NumberU64 () < origin {
521
658
// Print progress logs if long enough time elapsed
522
659
if time .Since (logged ) > 8 * time .Second {
523
- log .Info ("Regenerating historical state" , "block" , block .NumberU64 ()+ 1 , "target" , origin , "elapsed" , time .Since (start ))
660
+ log .Info ("Regenerating historical state" , "block" , block .NumberU64 ()+ 1 , "target" , origin , "remaining" , origin - block . NumberU64 () - 1 , " elapsed" , time .Since (start ))
524
661
logged = time .Now ()
525
662
}
526
663
// Retrieve the next block to regenerate and process it
@@ -529,15 +666,15 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
529
666
}
530
667
_ , _ , _ , err := api .eth .blockchain .Processor ().Process (block , statedb , vm.Config {})
531
668
if err != nil {
532
- return nil , err
669
+ return nil , fmt . Errorf ( "processing block %d failed: %v" , block . NumberU64 (), err )
533
670
}
534
671
// Finalize the state so any modifications are written to the trie
535
- root , err := statedb .Commit (true )
672
+ root , err := statedb .Commit (api . eth . blockchain . Config (). IsEIP158 ( block . Number ()) )
536
673
if err != nil {
537
674
return nil , err
538
675
}
539
676
if err := statedb .Reset (root ); err != nil {
540
- return nil , err
677
+ return nil , fmt . Errorf ( "state reset after block %d failed: %v" , block . NumberU64 (), err )
541
678
}
542
679
database .TrieDB ().Reference (root , common.Hash {})
543
680
if proot != (common.Hash {}) {
@@ -556,7 +693,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
556
693
// Retrieve the transaction and assemble its EVM context
557
694
tx , blockHash , _ , index := rawdb .ReadTransaction (api .eth .ChainDb (), hash )
558
695
if tx == nil {
559
- return nil , fmt .Errorf ("transaction %x not found" , hash )
696
+ return nil , fmt .Errorf ("transaction %# x not found" , hash )
560
697
}
561
698
reexec := defaultTraceReexec
562
699
if config != nil && config .Reexec != nil {
@@ -636,11 +773,11 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
636
773
// Create the parent state database
637
774
block := api .eth .blockchain .GetBlockByHash (blockHash )
638
775
if block == nil {
639
- return nil , vm.Context {}, nil , fmt .Errorf ("block %x not found" , blockHash )
776
+ return nil , vm.Context {}, nil , fmt .Errorf ("block %# x not found" , blockHash )
640
777
}
641
778
parent := api .eth .blockchain .GetBlock (block .ParentHash (), block .NumberU64 ()- 1 )
642
779
if parent == nil {
643
- return nil , vm.Context {}, nil , fmt .Errorf ("parent %x not found" , block .ParentHash ())
780
+ return nil , vm.Context {}, nil , fmt .Errorf ("parent %# x not found" , block .ParentHash ())
644
781
}
645
782
statedb , err := api .computeStateDB (parent , reexec )
646
783
if err != nil {
@@ -659,10 +796,10 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
659
796
// Not yet the searched for transaction, execute on top of the current state
660
797
vmenv := vm .NewEVM (context , statedb , api .config , vm.Config {})
661
798
if _ , _ , _ , err := core .ApplyMessage (vmenv , msg , new (core.GasPool ).AddGas (tx .Gas ())); err != nil {
662
- return nil , vm.Context {}, nil , fmt .Errorf ("tx % x failed: %v" , tx .Hash (), err )
799
+ return nil , vm.Context {}, nil , fmt .Errorf ("transaction %# x failed: %v" , tx .Hash (), err )
663
800
}
664
801
// Ensure any modifications are committed to the state
665
802
statedb .Finalise (true )
666
803
}
667
- return nil , vm.Context {}, nil , fmt .Errorf ("tx index %d out of range for block %x" , txIndex , blockHash )
804
+ return nil , vm.Context {}, nil , fmt .Errorf ("transaction index %d out of range for block %# x" , txIndex , blockHash )
668
805
}
0 commit comments