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