gitlab.com/flarenetwork/coreth@v0.1.1/eth/tracers/api.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2021 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package tracers
    28  
    29  import (
    30  	"bytes"
    31  	"context"
    32  	"errors"
    33  	"fmt"
    34  	"math/big"
    35  	"runtime"
    36  	"sync"
    37  	"time"
    38  
    39  	"github.com/ethereum/go-ethereum/common"
    40  	"github.com/ethereum/go-ethereum/common/hexutil"
    41  	"github.com/ethereum/go-ethereum/ethdb"
    42  	"github.com/ethereum/go-ethereum/log"
    43  	"github.com/ethereum/go-ethereum/rlp"
    44  	"gitlab.com/flarenetwork/coreth/consensus"
    45  	"gitlab.com/flarenetwork/coreth/core"
    46  	"gitlab.com/flarenetwork/coreth/core/state"
    47  	"gitlab.com/flarenetwork/coreth/core/types"
    48  	"gitlab.com/flarenetwork/coreth/core/vm"
    49  	"gitlab.com/flarenetwork/coreth/internal/ethapi"
    50  	"gitlab.com/flarenetwork/coreth/params"
    51  	"gitlab.com/flarenetwork/coreth/rpc"
    52  )
    53  
    54  const (
    55  	// defaultTraceTimeout is the amount of time a single transaction can execute
    56  	// by default before being forcefully aborted.
    57  	defaultTraceTimeout = 5 * time.Second
    58  
    59  	// defaultTraceReexec is the number of blocks the tracer is willing to go back
    60  	// and reexecute to produce missing historical state necessary to run a specific
    61  	// trace.
    62  	defaultTraceReexec = uint64(128)
    63  )
    64  
    65  // Backend interface provides the common API services (that are provided by
    66  // both full and light clients) with access to necessary functions.
    67  type Backend interface {
    68  	HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
    69  	HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
    70  	BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
    71  	BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
    72  	GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
    73  	RPCGasCap() uint64
    74  	ChainConfig() *params.ChainConfig
    75  	Engine() consensus.Engine
    76  	ChainDb() ethdb.Database
    77  	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error)
    78  	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
    79  }
    80  
    81  // API is the collection of tracing APIs exposed over the private debugging endpoint.
    82  type API struct {
    83  	backend Backend
    84  }
    85  
    86  // NewAPI creates a new API definition for the tracing methods of the Ethereum service.
    87  func NewAPI(backend Backend) *API {
    88  	return &API{backend: backend}
    89  }
    90  
    91  type chainContext struct {
    92  	api *API
    93  	ctx context.Context
    94  }
    95  
    96  func (context *chainContext) Engine() consensus.Engine {
    97  	return context.api.backend.Engine()
    98  }
    99  
   100  func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
   101  	header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
   102  	if err != nil {
   103  		return nil
   104  	}
   105  	if header.Hash() == hash {
   106  		return header
   107  	}
   108  	header, err = context.api.backend.HeaderByHash(context.ctx, hash)
   109  	if err != nil {
   110  		return nil
   111  	}
   112  	return header
   113  }
   114  
   115  // chainContext construts the context reader which is used by the evm for reading
   116  // the necessary chain context.
   117  func (api *API) chainContext(ctx context.Context) core.ChainContext {
   118  	return &chainContext{api: api, ctx: ctx}
   119  }
   120  
   121  // blockByNumber is the wrapper of the chain access function offered by the backend.
   122  // It will return an error if the block is not found.
   123  func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
   124  	block, err := api.backend.BlockByNumber(ctx, number)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	if block == nil {
   129  		return nil, fmt.Errorf("block #%d not found", number)
   130  	}
   131  	return block, nil
   132  }
   133  
   134  // blockByHash is the wrapper of the chain access function offered by the backend.
   135  // It will return an error if the block is not found.
   136  func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
   137  	block, err := api.backend.BlockByHash(ctx, hash)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	if block == nil {
   142  		return nil, fmt.Errorf("block %s not found", hash.Hex())
   143  	}
   144  	return block, nil
   145  }
   146  
   147  // blockByNumberAndHash is the wrapper of the chain access function offered by
   148  // the backend. It will return an error if the block is not found.
   149  //
   150  // Note this function is friendly for the light client which can only retrieve the
   151  // historical(before the CHT) header/block by number.
   152  func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) {
   153  	block, err := api.blockByNumber(ctx, number)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	if block.Hash() == hash {
   158  		return block, nil
   159  	}
   160  	return api.blockByHash(ctx, hash)
   161  }
   162  
   163  // TraceConfig holds extra parameters to trace functions.
   164  type TraceConfig struct {
   165  	*vm.LogConfig
   166  	Tracer  *string
   167  	Timeout *string
   168  	Reexec  *uint64
   169  }
   170  
   171  // TraceCallConfig is the config for traceCall API. It holds one more
   172  // field to override the state for tracing.
   173  type TraceCallConfig struct {
   174  	*vm.LogConfig
   175  	Tracer         *string
   176  	Timeout        *string
   177  	Reexec         *uint64
   178  	StateOverrides *ethapi.StateOverride
   179  }
   180  
   181  // StdTraceConfig holds extra parameters to standard-json trace functions.
   182  type StdTraceConfig struct {
   183  	vm.LogConfig
   184  	Reexec *uint64
   185  	TxHash common.Hash
   186  }
   187  
   188  // txTraceResult is the result of a single transaction trace.
   189  type txTraceResult struct {
   190  	Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
   191  	Error  string      `json:"error,omitempty"`  // Trace failure produced by the tracer
   192  }
   193  
   194  // blockTraceTask represents a single block trace task when an entire chain is
   195  // being traced.
   196  type blockTraceTask struct {
   197  	statedb *state.StateDB   // Intermediate state prepped for tracing
   198  	block   *types.Block     // Block to trace the transactions from
   199  	rootref common.Hash      // Trie root reference held for this task
   200  	results []*txTraceResult // Trace results procudes by the task
   201  }
   202  
   203  // blockTraceResult represets the results of tracing a single block when an entire
   204  // chain is being traced.
   205  type blockTraceResult struct {
   206  	Block  hexutil.Uint64   `json:"block"`  // Block number corresponding to this trace
   207  	Hash   common.Hash      `json:"hash"`   // Block hash corresponding to this trace
   208  	Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
   209  }
   210  
   211  // txTraceTask represents a single transaction trace task when an entire block
   212  // is being traced.
   213  type txTraceTask struct {
   214  	statedb *state.StateDB // Intermediate state prepped for tracing
   215  	index   int            // Transaction offset in the block
   216  }
   217  
   218  // TraceChain returns the structured logs created during the execution of EVM
   219  // between two blocks (excluding start) and returns them as a JSON object.
   220  func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { // Fetch the block interval that we want to trace
   221  	from, err := api.blockByNumber(ctx, start)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	to, err := api.blockByNumber(ctx, end)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	if from.Number().Cmp(to.Number()) >= 0 {
   230  		return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start)
   231  	}
   232  	return api.traceChain(ctx, from, to, config)
   233  }
   234  
   235  // traceChain configures a new tracer according to the provided configuration, and
   236  // executes all the transactions contained within. The return value will be one item
   237  // per transaction, dependent on the requested tracer.
   238  func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
   239  	// Tracing a chain is a **long** operation, only do with subscriptions
   240  	notifier, supported := rpc.NotifierFromContext(ctx)
   241  	if !supported {
   242  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
   243  	}
   244  	sub := notifier.CreateSubscription()
   245  
   246  	// Prepare all the states for tracing. Note this procedure can take very
   247  	// long time. Timeout mechanism is necessary.
   248  	reexec := defaultTraceReexec
   249  	if config != nil && config.Reexec != nil {
   250  		reexec = *config.Reexec
   251  	}
   252  	blocks := int(end.NumberU64() - start.NumberU64())
   253  	threads := runtime.NumCPU()
   254  	if threads > blocks {
   255  		threads = blocks
   256  	}
   257  	var (
   258  		pend     = new(sync.WaitGroup)
   259  		tasks    = make(chan *blockTraceTask, threads)
   260  		results  = make(chan *blockTraceTask, threads)
   261  		localctx = context.Background()
   262  	)
   263  	for th := 0; th < threads; th++ {
   264  		pend.Add(1)
   265  		go func() {
   266  			defer pend.Done()
   267  
   268  			// Fetch and execute the next block trace tasks
   269  			for task := range tasks {
   270  				signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), new(big.Int).SetUint64(task.block.Time()))
   271  				blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
   272  				// Trace all the transactions contained within
   273  				for i, tx := range task.block.Transactions() {
   274  					msg, _ := tx.AsMessage(signer, task.block.BaseFee())
   275  					txctx := &Context{
   276  						BlockHash: task.block.Hash(),
   277  						TxIndex:   i,
   278  						TxHash:    tx.Hash(),
   279  					}
   280  					res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
   281  					if err != nil {
   282  						task.results[i] = &txTraceResult{Error: err.Error()}
   283  						log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   284  						break
   285  					}
   286  					// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
   287  					task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
   288  					task.results[i] = &txTraceResult{Result: res}
   289  				}
   290  				// Stream the result back to the user or abort on teardown
   291  				select {
   292  				case results <- task:
   293  				case <-notifier.Closed():
   294  					return
   295  				}
   296  			}
   297  		}()
   298  	}
   299  	// Start a goroutine to feed all the blocks into the tracers
   300  	begin := time.Now()
   301  
   302  	go func() {
   303  		var (
   304  			logged  time.Time
   305  			number  uint64
   306  			traced  uint64
   307  			failed  error
   308  			parent  common.Hash
   309  			statedb *state.StateDB
   310  		)
   311  		// Ensure everything is properly cleaned up on any exit path
   312  		defer func() {
   313  			close(tasks)
   314  			pend.Wait()
   315  
   316  			switch {
   317  			case failed != nil:
   318  				log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
   319  			case number < end.NumberU64():
   320  				log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin))
   321  			default:
   322  				log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
   323  			}
   324  			close(results)
   325  		}()
   326  		// Feed all the blocks both into the tracer, as well as fast process concurrently
   327  		for number = start.NumberU64(); number < end.NumberU64(); number++ {
   328  			// Stop tracing if interruption was requested
   329  			select {
   330  			case <-notifier.Closed():
   331  				return
   332  			default:
   333  			}
   334  			// Print progress logs if long enough time elapsed
   335  			if time.Since(logged) > 8*time.Second {
   336  				logged = time.Now()
   337  				log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
   338  			}
   339  			// Retrieve the parent state to trace on top
   340  			block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
   341  			if err != nil {
   342  				failed = err
   343  				break
   344  			}
   345  			// Prepare the statedb for tracing. Don't use the live database for
   346  			// tracing to avoid persisting state junks into the database.
   347  			statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false)
   348  			if err != nil {
   349  				failed = err
   350  				break
   351  			}
   352  			if statedb.Database().TrieDB() != nil {
   353  				// Hold the reference for tracer, will be released at the final stage
   354  				statedb.Database().TrieDB().Reference(block.Root(), common.Hash{})
   355  
   356  				// Release the parent state because it's already held by the tracer
   357  				if parent != (common.Hash{}) {
   358  					statedb.Database().TrieDB().Dereference(parent)
   359  				}
   360  			}
   361  			parent = block.Root()
   362  
   363  			next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
   364  			if err != nil {
   365  				failed = err
   366  				break
   367  			}
   368  			// Send the block over to the concurrent tracers (if not in the fast-forward phase)
   369  			txs := next.Transactions()
   370  			select {
   371  			case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
   372  			case <-notifier.Closed():
   373  				return
   374  			}
   375  			traced += uint64(len(txs))
   376  		}
   377  	}()
   378  
   379  	// Keep reading the trace results and stream the to the user
   380  	go func() {
   381  		var (
   382  			done = make(map[uint64]*blockTraceResult)
   383  			next = start.NumberU64() + 1
   384  		)
   385  		for res := range results {
   386  			// Queue up next received result
   387  			result := &blockTraceResult{
   388  				Block:  hexutil.Uint64(res.block.NumberU64()),
   389  				Hash:   res.block.Hash(),
   390  				Traces: res.results,
   391  			}
   392  			done[uint64(result.Block)] = result
   393  
   394  			// Dereference any parent tries held in memory by this task
   395  			if res.statedb.Database().TrieDB() != nil {
   396  				res.statedb.Database().TrieDB().Dereference(res.rootref)
   397  			}
   398  			// Stream completed traces to the user, aborting on the first error
   399  			for result, ok := done[next]; ok; result, ok = done[next] {
   400  				if len(result.Traces) > 0 || next == end.NumberU64() {
   401  					notifier.Notify(sub.ID, result)
   402  				}
   403  				delete(done, next)
   404  				next++
   405  			}
   406  		}
   407  	}()
   408  	return sub, nil
   409  }
   410  
   411  // TraceBlockByNumber returns the structured logs created during the execution of
   412  // EVM and returns them as a JSON object.
   413  func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
   414  	block, err := api.blockByNumber(ctx, number)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  	return api.traceBlock(ctx, block, config)
   419  }
   420  
   421  // TraceBlockByHash returns the structured logs created during the execution of
   422  // EVM and returns them as a JSON object.
   423  func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   424  	block, err := api.blockByHash(ctx, hash)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  	return api.traceBlock(ctx, block, config)
   429  }
   430  
   431  // TraceBlock returns the structured logs created during the execution of EVM
   432  // and returns them as a JSON object.
   433  func (api *API) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) {
   434  	block := new(types.Block)
   435  	if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
   436  		return nil, fmt.Errorf("could not decode block: %v", err)
   437  	}
   438  	return api.traceBlock(ctx, block, config)
   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 *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
   445  	if block.NumberU64() == 0 {
   446  		return nil, errors.New("genesis is not traceable")
   447  	}
   448  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  	reexec := defaultTraceReexec
   453  	if config != nil && config.Reexec != nil {
   454  		reexec = *config.Reexec
   455  	}
   456  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	// Execute all the transaction contained within the block concurrently
   461  	var (
   462  		signer  = types.MakeSigner(api.backend.ChainConfig(), block.Number(), new(big.Int).SetUint64(block.Time()))
   463  		txs     = block.Transactions()
   464  		results = make([]*txTraceResult, len(txs))
   465  
   466  		pend = new(sync.WaitGroup)
   467  		jobs = make(chan *txTraceTask, len(txs))
   468  	)
   469  	threads := runtime.NumCPU()
   470  	if threads > len(txs) {
   471  		threads = len(txs)
   472  	}
   473  	blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   474  	blockHash := block.Hash()
   475  	for th := 0; th < threads; th++ {
   476  		pend.Add(1)
   477  		go func() {
   478  			defer pend.Done()
   479  			// Fetch and execute the next transaction trace tasks
   480  			for task := range jobs {
   481  				msg, _ := txs[task.index].AsMessage(signer, block.BaseFee())
   482  				txctx := &Context{
   483  					BlockHash: blockHash,
   484  					TxIndex:   task.index,
   485  					TxHash:    txs[task.index].Hash(),
   486  				}
   487  				res, err := api.traceTx(ctx, msg, txctx, blockCtx, 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, block.BaseFee())
   504  		statedb.Prepare(tx.Hash(), i)
   505  		vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
   506  		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
   507  			failed = err
   508  			break
   509  		}
   510  		// Finalize the state so any modifications are written to the trie
   511  		// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
   512  		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
   513  	}
   514  	close(jobs)
   515  	pend.Wait()
   516  
   517  	// If execution failed in between, abort
   518  	if failed != nil {
   519  		return nil, failed
   520  	}
   521  	return results, nil
   522  }
   523  
   524  // TraceTransaction returns the structured logs created during the execution of EVM
   525  // and returns them as a JSON object.
   526  func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
   527  	_, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  	// It shouldn't happen in practice.
   532  	if blockNumber == 0 {
   533  		return nil, errors.New("genesis is not traceable")
   534  	}
   535  	reexec := defaultTraceReexec
   536  	if config != nil && config.Reexec != nil {
   537  		reexec = *config.Reexec
   538  	}
   539  	block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
   540  	if err != nil {
   541  		return nil, err
   542  	}
   543  	msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
   544  	if err != nil {
   545  		return nil, err
   546  	}
   547  	txctx := &Context{
   548  		BlockHash: blockHash,
   549  		TxIndex:   int(index),
   550  		TxHash:    hash,
   551  	}
   552  	return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
   553  }
   554  
   555  // TraceCall lets you trace a given eth_call. It collects the structured logs
   556  // created during the execution of EVM if the given transaction was added on
   557  // top of the provided block and returns them as a JSON object.
   558  // You can provide -2 as a block number to trace on top of the pending block.
   559  func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
   560  	// Try to retrieve the specified block
   561  	var (
   562  		err   error
   563  		block *types.Block
   564  	)
   565  	if hash, ok := blockNrOrHash.Hash(); ok {
   566  		block, err = api.blockByHash(ctx, hash)
   567  	} else if number, ok := blockNrOrHash.Number(); ok {
   568  		block, err = api.blockByNumber(ctx, number)
   569  	} else {
   570  		return nil, errors.New("invalid arguments; neither block nor hash specified")
   571  	}
   572  	if err != nil {
   573  		return nil, err
   574  	}
   575  	// try to recompute the state
   576  	reexec := defaultTraceReexec
   577  	if config != nil && config.Reexec != nil {
   578  		reexec = *config.Reexec
   579  	}
   580  	statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true)
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  	// Apply the customized state rules if required.
   585  	if config != nil {
   586  		if err := config.StateOverrides.Apply(statedb); err != nil {
   587  			return nil, err
   588  		}
   589  	}
   590  	// Execute the trace
   591  	msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  	vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   596  
   597  	var traceConfig *TraceConfig
   598  	if config != nil {
   599  		traceConfig = &TraceConfig{
   600  			LogConfig: config.LogConfig,
   601  			Tracer:    config.Tracer,
   602  			Timeout:   config.Timeout,
   603  			Reexec:    config.Reexec,
   604  		}
   605  	}
   606  	return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
   607  }
   608  
   609  // traceTx configures a new tracer according to the provided configuration, and
   610  // executes the given message in the provided environment. The return value will
   611  // be tracer dependent.
   612  func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
   613  	// Assemble the structured logger or the JavaScript tracer
   614  	var (
   615  		tracer    vm.Tracer
   616  		err       error
   617  		txContext = core.NewEVMTxContext(message)
   618  	)
   619  	switch {
   620  	case config != nil && config.Tracer != nil:
   621  		// Define a meaningful timeout of a single transaction trace
   622  		timeout := defaultTraceTimeout
   623  		if config.Timeout != nil {
   624  			if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
   625  				return nil, err
   626  			}
   627  		}
   628  		// Constuct the JavaScript tracer to execute with
   629  		if tracer, err = New(*config.Tracer, txctx); err != nil {
   630  			return nil, err
   631  		}
   632  		// Handle timeouts and RPC cancellations
   633  		deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
   634  		go func() {
   635  			<-deadlineCtx.Done()
   636  			if deadlineCtx.Err() == context.DeadlineExceeded {
   637  				tracer.(*Tracer).Stop(errors.New("execution timeout"))
   638  			}
   639  		}()
   640  		defer cancel()
   641  
   642  	case config == nil:
   643  		tracer = vm.NewStructLogger(nil)
   644  
   645  	default:
   646  		tracer = vm.NewStructLogger(config.LogConfig)
   647  	}
   648  	// Run the transaction with tracing enabled.
   649  	vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
   650  
   651  	// Call Prepare to clear out the statedb access list
   652  	statedb.Prepare(txctx.TxHash, txctx.TxIndex)
   653  
   654  	result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
   655  	if err != nil {
   656  		return nil, fmt.Errorf("tracing failed: %w", err)
   657  	}
   658  
   659  	// Depending on the tracer type, format and return the output.
   660  	switch tracer := tracer.(type) {
   661  	case *vm.StructLogger:
   662  		// If the result contains a revert reason, return it.
   663  		returnVal := fmt.Sprintf("%x", result.Return())
   664  		if len(result.Revert()) > 0 {
   665  			returnVal = fmt.Sprintf("%x", result.Revert())
   666  		}
   667  		return &ethapi.ExecutionResult{
   668  			Gas:         result.UsedGas,
   669  			Failed:      result.Failed(),
   670  			ReturnValue: returnVal,
   671  			StructLogs:  ethapi.FormatLogs(tracer.StructLogs()),
   672  		}, nil
   673  
   674  	case *Tracer:
   675  		return tracer.GetResult()
   676  
   677  	default:
   678  		panic(fmt.Sprintf("bad tracer type %T", tracer))
   679  	}
   680  }
   681  
   682  // APIs return the collection of RPC services the tracer package offers.
   683  func APIs(backend Backend) []rpc.API {
   684  	// Append all the local APIs and return
   685  	return []rpc.API{
   686  		{
   687  			Namespace: "debug",
   688  			Version:   "1.0",
   689  			Service:   NewAPI(backend),
   690  			Public:    false,
   691  		},
   692  	}
   693  }