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