github.com/authcall/reference-optimistic-geth@v0.0.0-20220816224302-06313bfeb8d2/eth/tracers/api.go (about)

     1  // Copyright 2021 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 tracers
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"os"
    26  	"runtime"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/common/hexutil"
    32  	"github.com/ethereum/go-ethereum/consensus"
    33  	"github.com/ethereum/go-ethereum/core"
    34  	"github.com/ethereum/go-ethereum/core/rawdb"
    35  	"github.com/ethereum/go-ethereum/core/state"
    36  	"github.com/ethereum/go-ethereum/core/types"
    37  	"github.com/ethereum/go-ethereum/core/vm"
    38  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    39  	"github.com/ethereum/go-ethereum/ethdb"
    40  	"github.com/ethereum/go-ethereum/internal/ethapi"
    41  	"github.com/ethereum/go-ethereum/log"
    42  	"github.com/ethereum/go-ethereum/params"
    43  	"github.com/ethereum/go-ethereum/rlp"
    44  	"github.com/ethereum/go-ethereum/rpc"
    45  )
    46  
    47  const (
    48  	// defaultTraceTimeout is the amount of time a single transaction can execute
    49  	// by default before being forcefully aborted.
    50  	defaultTraceTimeout = 5 * time.Second
    51  
    52  	// defaultTraceReexec is the number of blocks the tracer is willing to go back
    53  	// and reexecute to produce missing historical state necessary to run a specific
    54  	// trace.
    55  	defaultTraceReexec = uint64(128)
    56  
    57  	// defaultTracechainMemLimit is the size of the triedb, at which traceChain
    58  	// switches over and tries to use a disk-backed database instead of building
    59  	// on top of memory.
    60  	// For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
    61  	// will only be found every ~15K blocks or so.
    62  	defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024)
    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 returns the state corresponding to the stateroot of the block.
    78  	// N.B: For executing transactions on block N, the required stateRoot is block N-1,
    79  	// so this method should be called with the parent.
    80  	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error)
    81  	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
    82  }
    83  
    84  // API is the collection of tracing APIs exposed over the private debugging endpoint.
    85  type API struct {
    86  	backend Backend
    87  }
    88  
    89  // NewAPI creates a new API definition for the tracing methods of the Ethereum service.
    90  func NewAPI(backend Backend) *API {
    91  	return &API{backend: backend}
    92  }
    93  
    94  type chainContext struct {
    95  	api *API
    96  	ctx context.Context
    97  }
    98  
    99  func (context *chainContext) Engine() consensus.Engine {
   100  	return context.api.backend.Engine()
   101  }
   102  
   103  func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
   104  	header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
   105  	if err != nil {
   106  		return nil
   107  	}
   108  	if header.Hash() == hash {
   109  		return header
   110  	}
   111  	header, err = context.api.backend.HeaderByHash(context.ctx, hash)
   112  	if err != nil {
   113  		return nil
   114  	}
   115  	return header
   116  }
   117  
   118  // chainContext construts the context reader which is used by the evm for reading
   119  // the necessary chain context.
   120  func (api *API) chainContext(ctx context.Context) core.ChainContext {
   121  	return &chainContext{api: api, ctx: ctx}
   122  }
   123  
   124  // blockByNumber is the wrapper of the chain access function offered by the backend.
   125  // It will return an error if the block is not found.
   126  func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
   127  	block, err := api.backend.BlockByNumber(ctx, number)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	if block == nil {
   132  		return nil, fmt.Errorf("block #%d not found", number)
   133  	}
   134  	return block, nil
   135  }
   136  
   137  // blockByHash is the wrapper of the chain access function offered by the backend.
   138  // It will return an error if the block is not found.
   139  func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
   140  	block, err := api.backend.BlockByHash(ctx, hash)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	if block == nil {
   145  		return nil, fmt.Errorf("block %s not found", hash.Hex())
   146  	}
   147  	return block, nil
   148  }
   149  
   150  // blockByNumberAndHash is the wrapper of the chain access function offered by
   151  // the backend. It will return an error if the block is not found.
   152  //
   153  // Note this function is friendly for the light client which can only retrieve the
   154  // historical(before the CHT) header/block by number.
   155  func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) {
   156  	block, err := api.blockByNumber(ctx, number)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	if block.Hash() == hash {
   161  		return block, nil
   162  	}
   163  	return api.blockByHash(ctx, hash)
   164  }
   165  
   166  // TraceConfig holds extra parameters to trace functions.
   167  type TraceConfig struct {
   168  	*logger.Config
   169  	Tracer  *string
   170  	Timeout *string
   171  	Reexec  *uint64
   172  }
   173  
   174  // TraceCallConfig is the config for traceCall API. It holds one more
   175  // field to override the state for tracing.
   176  type TraceCallConfig struct {
   177  	*logger.Config
   178  	Tracer         *string
   179  	Timeout        *string
   180  	Reexec         *uint64
   181  	StateOverrides *ethapi.StateOverride
   182  	BlockOverrides *ethapi.BlockOverrides
   183  }
   184  
   185  // StdTraceConfig holds extra parameters to standard-json trace functions.
   186  type StdTraceConfig struct {
   187  	logger.Config
   188  	Reexec *uint64
   189  	TxHash common.Hash
   190  }
   191  
   192  // txTraceResult is the result of a single transaction trace.
   193  type txTraceResult struct {
   194  	Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
   195  	Error  string      `json:"error,omitempty"`  // Trace failure produced by the tracer
   196  }
   197  
   198  // blockTraceTask represents a single block trace task when an entire chain is
   199  // being traced.
   200  type blockTraceTask struct {
   201  	statedb *state.StateDB   // Intermediate state prepped for tracing
   202  	block   *types.Block     // Block to trace the transactions from
   203  	rootref common.Hash      // Trie root reference held for this task
   204  	results []*txTraceResult // Trace results procudes by the task
   205  }
   206  
   207  // blockTraceResult represets the results of tracing a single block when an entire
   208  // chain is being traced.
   209  type blockTraceResult struct {
   210  	Block  hexutil.Uint64   `json:"block"`  // Block number corresponding to this trace
   211  	Hash   common.Hash      `json:"hash"`   // Block hash corresponding to this trace
   212  	Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
   213  }
   214  
   215  // txTraceTask represents a single transaction trace task when an entire block
   216  // is being traced.
   217  type txTraceTask struct {
   218  	statedb *state.StateDB // Intermediate state prepped for tracing
   219  	index   int            // Transaction offset in the block
   220  }
   221  
   222  // TraceChain returns the structured logs created during the execution of EVM
   223  // between two blocks (excluding start) and returns them as a JSON object.
   224  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
   225  	from, err := api.blockByNumber(ctx, start)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	to, err := api.blockByNumber(ctx, end)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	if from.Number().Cmp(to.Number()) >= 0 {
   234  		return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start)
   235  	}
   236  	return api.traceChain(ctx, from, to, config)
   237  }
   238  
   239  // traceChain configures a new tracer according to the provided configuration, and
   240  // executes all the transactions contained within. The return value will be one item
   241  // per transaction, dependent on the requested tracer.
   242  func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
   243  	// Tracing a chain is a **long** operation, only do with subscriptions
   244  	notifier, supported := rpc.NotifierFromContext(ctx)
   245  	if !supported {
   246  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
   247  	}
   248  	sub := notifier.CreateSubscription()
   249  
   250  	// Prepare all the states for tracing. Note this procedure can take very
   251  	// long time. Timeout mechanism is necessary.
   252  	reexec := defaultTraceReexec
   253  	if config != nil && config.Reexec != nil {
   254  		reexec = *config.Reexec
   255  	}
   256  	blocks := int(end.NumberU64() - start.NumberU64())
   257  	threads := runtime.NumCPU()
   258  	if threads > blocks {
   259  		threads = blocks
   260  	}
   261  	var (
   262  		pend     = new(sync.WaitGroup)
   263  		tasks    = make(chan *blockTraceTask, threads)
   264  		results  = make(chan *blockTraceTask, threads)
   265  		localctx = context.Background()
   266  	)
   267  	for th := 0; th < threads; th++ {
   268  		pend.Add(1)
   269  		go func() {
   270  			defer pend.Done()
   271  
   272  			// Fetch and execute the next block trace tasks
   273  			for task := range tasks {
   274  				signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
   275  				blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
   276  				blockCtx.L1CostFunc = core.NewL1CostFunc(api.backend.ChainConfig(), task.statedb)
   277  				// Trace all the transactions contained within
   278  				for i, tx := range task.block.Transactions() {
   279  					msg, _ := tx.AsMessage(signer, task.block.BaseFee())
   280  					txctx := &Context{
   281  						BlockHash: task.block.Hash(),
   282  						TxIndex:   i,
   283  						TxHash:    tx.Hash(),
   284  					}
   285  					res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
   286  					if err != nil {
   287  						task.results[i] = &txTraceResult{Error: err.Error()}
   288  						log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   289  						break
   290  					}
   291  					// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
   292  					task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
   293  					task.results[i] = &txTraceResult{Result: res}
   294  				}
   295  				// Stream the result back to the user or abort on teardown
   296  				select {
   297  				case results <- task:
   298  				case <-notifier.Closed():
   299  					return
   300  				}
   301  			}
   302  		}()
   303  	}
   304  	// Start a goroutine to feed all the blocks into the tracers
   305  	var (
   306  		begin     = time.Now()
   307  		derefTodo []common.Hash // list of hashes to dereference from the db
   308  		derefsMu  sync.Mutex    // mutex for the derefs
   309  	)
   310  
   311  	go func() {
   312  		var (
   313  			logged  time.Time
   314  			number  uint64
   315  			traced  uint64
   316  			failed  error
   317  			parent  common.Hash
   318  			statedb *state.StateDB
   319  		)
   320  		// Ensure everything is properly cleaned up on any exit path
   321  		defer func() {
   322  			close(tasks)
   323  			pend.Wait()
   324  
   325  			switch {
   326  			case failed != nil:
   327  				log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
   328  			case number < end.NumberU64():
   329  				log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin))
   330  			default:
   331  				log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
   332  			}
   333  			close(results)
   334  		}()
   335  		var preferDisk bool
   336  		// Feed all the blocks both into the tracer, as well as fast process concurrently
   337  		for number = start.NumberU64(); number < end.NumberU64(); number++ {
   338  			// Stop tracing if interruption was requested
   339  			select {
   340  			case <-notifier.Closed():
   341  				return
   342  			default:
   343  			}
   344  			// clean out any derefs
   345  			derefsMu.Lock()
   346  			for _, h := range derefTodo {
   347  				statedb.Database().TrieDB().Dereference(h)
   348  			}
   349  			derefTodo = derefTodo[:0]
   350  			derefsMu.Unlock()
   351  
   352  			// Print progress logs if long enough time elapsed
   353  			if time.Since(logged) > 8*time.Second {
   354  				logged = time.Now()
   355  				log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
   356  			}
   357  			// Retrieve the parent state to trace on top
   358  			block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
   359  			if err != nil {
   360  				failed = err
   361  				break
   362  			}
   363  			// Prepare the statedb for tracing. Don't use the live database for
   364  			// tracing to avoid persisting state junks into the database.
   365  			statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk)
   366  			if err != nil {
   367  				failed = err
   368  				break
   369  			}
   370  			if trieDb := statedb.Database().TrieDB(); trieDb != nil {
   371  				// Hold the reference for tracer, will be released at the final stage
   372  				trieDb.Reference(block.Root(), common.Hash{})
   373  
   374  				// Release the parent state because it's already held by the tracer
   375  				if parent != (common.Hash{}) {
   376  					trieDb.Dereference(parent)
   377  				}
   378  				// Prefer disk if the trie db memory grows too much
   379  				s1, s2 := trieDb.Size()
   380  				if !preferDisk && (s1+s2) > defaultTracechainMemLimit {
   381  					log.Info("Switching to prefer-disk mode for tracing", "size", s1+s2)
   382  					preferDisk = true
   383  				}
   384  			}
   385  			parent = block.Root()
   386  
   387  			next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
   388  			if err != nil {
   389  				failed = err
   390  				break
   391  			}
   392  			// Send the block over to the concurrent tracers (if not in the fast-forward phase)
   393  			txs := next.Transactions()
   394  			select {
   395  			case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
   396  			case <-notifier.Closed():
   397  				return
   398  			}
   399  			traced += uint64(len(txs))
   400  		}
   401  	}()
   402  
   403  	// Keep reading the trace results and stream the to the user
   404  	go func() {
   405  		var (
   406  			done = make(map[uint64]*blockTraceResult)
   407  			next = start.NumberU64() + 1
   408  		)
   409  		for res := range results {
   410  			// Queue up next received result
   411  			result := &blockTraceResult{
   412  				Block:  hexutil.Uint64(res.block.NumberU64()),
   413  				Hash:   res.block.Hash(),
   414  				Traces: res.results,
   415  			}
   416  			// Schedule any parent tries held in memory by this task for dereferencing
   417  			done[uint64(result.Block)] = result
   418  			derefsMu.Lock()
   419  			derefTodo = append(derefTodo, res.rootref)
   420  			derefsMu.Unlock()
   421  			// Stream completed traces to the user, aborting on the first error
   422  			for result, ok := done[next]; ok; result, ok = done[next] {
   423  				if len(result.Traces) > 0 || next == end.NumberU64() {
   424  					notifier.Notify(sub.ID, result)
   425  				}
   426  				delete(done, next)
   427  				next++
   428  			}
   429  		}
   430  	}()
   431  	return sub, nil
   432  }
   433  
   434  // TraceBlockByNumber returns the structured logs created during the execution of
   435  // EVM and returns them as a JSON object.
   436  func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
   437  	block, err := api.blockByNumber(ctx, number)
   438  	if err != nil {
   439  		return nil, err
   440  	}
   441  	return api.traceBlock(ctx, block, config)
   442  }
   443  
   444  // TraceBlockByHash returns the structured logs created during the execution of
   445  // EVM and returns them as a JSON object.
   446  func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   447  	block, err := api.blockByHash(ctx, hash)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	return api.traceBlock(ctx, block, config)
   452  }
   453  
   454  // TraceBlock returns the structured logs created during the execution of EVM
   455  // and returns them as a JSON object.
   456  func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
   457  	block := new(types.Block)
   458  	if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
   459  		return nil, fmt.Errorf("could not decode block: %v", err)
   460  	}
   461  	return api.traceBlock(ctx, block, config)
   462  }
   463  
   464  // TraceBlockFromFile returns the structured logs created during the execution of
   465  // EVM and returns them as a JSON object.
   466  func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) {
   467  	blob, err := os.ReadFile(file)
   468  	if err != nil {
   469  		return nil, fmt.Errorf("could not read file: %v", err)
   470  	}
   471  	return api.TraceBlock(ctx, blob, config)
   472  }
   473  
   474  // TraceBadBlock returns the structured logs created during the execution of
   475  // EVM against a block pulled from the pool of bad ones and returns them as a JSON
   476  // object.
   477  func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   478  	block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
   479  	if block == nil {
   480  		return nil, fmt.Errorf("bad block %#x not found", hash)
   481  	}
   482  	return api.traceBlock(ctx, block, config)
   483  }
   484  
   485  // StandardTraceBlockToFile dumps the structured logs created during the
   486  // execution of EVM to the local file system and returns a list of files
   487  // to the caller.
   488  func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   489  	block, err := api.blockByHash(ctx, hash)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  	return api.standardTraceBlockToFile(ctx, block, config)
   494  }
   495  
   496  // IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
   497  // of intermediate roots: the stateroot after each transaction.
   498  func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
   499  	block, _ := api.blockByHash(ctx, hash)
   500  	if block == nil {
   501  		// Check in the bad blocks
   502  		block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
   503  	}
   504  	if block == nil {
   505  		return nil, fmt.Errorf("block %#x not found", hash)
   506  	}
   507  	if block.NumberU64() == 0 {
   508  		return nil, errors.New("genesis is not traceable")
   509  	}
   510  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  	reexec := defaultTraceReexec
   515  	if config != nil && config.Reexec != nil {
   516  		reexec = *config.Reexec
   517  	}
   518  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   519  	if err != nil {
   520  		return nil, err
   521  	}
   522  	var (
   523  		roots              []common.Hash
   524  		signer             = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   525  		chainConfig        = api.backend.ChainConfig()
   526  		vmctx              = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   527  		deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
   528  	)
   529  	vmctx.L1CostFunc = core.NewL1CostFunc(chainConfig, statedb)
   530  	for i, tx := range block.Transactions() {
   531  		var (
   532  			msg, _    = tx.AsMessage(signer, block.BaseFee())
   533  			txContext = core.NewEVMTxContext(msg)
   534  			vmenv     = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
   535  		)
   536  		statedb.Prepare(tx.Hash(), i)
   537  		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
   538  			log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
   539  			// We intentionally don't return the error here: if we do, then the RPC server will not
   540  			// return the roots. Most likely, the caller already knows that a certain transaction fails to
   541  			// be included, but still want the intermediate roots that led to that point.
   542  			// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
   543  			// executable.
   544  			// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
   545  			return roots, nil
   546  		}
   547  		// calling IntermediateRoot will internally call Finalize on the state
   548  		// so any modifications are written to the trie
   549  		roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
   550  	}
   551  	return roots, nil
   552  }
   553  
   554  // StandardTraceBadBlockToFile dumps the structured logs created during the
   555  // execution of EVM against a block pulled from the pool of bad ones to the
   556  // local file system and returns a list of files to the caller.
   557  func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   558  	block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
   559  	if block == nil {
   560  		return nil, fmt.Errorf("bad block %#x not found", hash)
   561  	}
   562  	return api.standardTraceBlockToFile(ctx, block, config)
   563  }
   564  
   565  // traceBlock configures a new tracer according to the provided configuration, and
   566  // executes all the transactions contained within. The return value will be one item
   567  // per transaction, dependent on the requestd tracer.
   568  func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
   569  	if block.NumberU64() == 0 {
   570  		return nil, errors.New("genesis is not traceable")
   571  	}
   572  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  	reexec := defaultTraceReexec
   577  	if config != nil && config.Reexec != nil {
   578  		reexec = *config.Reexec
   579  	}
   580  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  	// Execute all the transaction contained within the block concurrently
   585  	var (
   586  		signer  = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   587  		txs     = block.Transactions()
   588  		results = make([]*txTraceResult, len(txs))
   589  
   590  		pend = new(sync.WaitGroup)
   591  		jobs = make(chan *txTraceTask, len(txs))
   592  	)
   593  	threads := runtime.NumCPU()
   594  	if threads > len(txs) {
   595  		threads = len(txs)
   596  	}
   597  	blockHash := block.Hash()
   598  	for th := 0; th < threads; th++ {
   599  		pend.Add(1)
   600  		go func() {
   601  			blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   602  			defer pend.Done()
   603  			blockCtx.L1CostFunc = core.NewL1CostFunc(api.backend.ChainConfig(), statedb)
   604  			// Fetch and execute the next transaction trace tasks
   605  			for task := range jobs {
   606  				msg, _ := txs[task.index].AsMessage(signer, block.BaseFee())
   607  				txctx := &Context{
   608  					BlockHash: blockHash,
   609  					TxIndex:   task.index,
   610  					TxHash:    txs[task.index].Hash(),
   611  				}
   612  				res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
   613  				if err != nil {
   614  					results[task.index] = &txTraceResult{Error: err.Error()}
   615  					continue
   616  				}
   617  				results[task.index] = &txTraceResult{Result: res}
   618  			}
   619  		}()
   620  	}
   621  	// Feed the transactions into the tracers and return
   622  	var failed error
   623  	blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   624  	blockCtx.L1CostFunc = core.NewL1CostFunc(api.backend.ChainConfig(), statedb)
   625  	for i, tx := range txs {
   626  		// Send the trace task over for execution
   627  		jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
   628  
   629  		// Generate the next state snapshot fast without tracing
   630  		msg, _ := tx.AsMessage(signer, block.BaseFee())
   631  		statedb.Prepare(tx.Hash(), i)
   632  		vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
   633  		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
   634  			failed = err
   635  			break
   636  		}
   637  		// Finalize the state so any modifications are written to the trie
   638  		// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
   639  		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
   640  	}
   641  	close(jobs)
   642  	pend.Wait()
   643  
   644  	// If execution failed in between, abort
   645  	if failed != nil {
   646  		return nil, failed
   647  	}
   648  	return results, nil
   649  }
   650  
   651  // standardTraceBlockToFile configures a new tracer which uses standard JSON output,
   652  // and traces either a full block or an individual transaction. The return value will
   653  // be one filename per transaction traced.
   654  func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
   655  	// If we're tracing a single transaction, make sure it's present
   656  	if config != nil && config.TxHash != (common.Hash{}) {
   657  		if !containsTx(block, config.TxHash) {
   658  			return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
   659  		}
   660  	}
   661  	if block.NumberU64() == 0 {
   662  		return nil, errors.New("genesis is not traceable")
   663  	}
   664  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  	reexec := defaultTraceReexec
   669  	if config != nil && config.Reexec != nil {
   670  		reexec = *config.Reexec
   671  	}
   672  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   673  	if err != nil {
   674  		return nil, err
   675  	}
   676  	// Retrieve the tracing configurations, or use default values
   677  	var (
   678  		logConfig logger.Config
   679  		txHash    common.Hash
   680  	)
   681  	if config != nil {
   682  		logConfig = config.Config
   683  		txHash = config.TxHash
   684  	}
   685  	logConfig.Debug = true
   686  
   687  	// Execute transaction, either tracing all or just the requested one
   688  	var (
   689  		dumps       []string
   690  		signer      = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   691  		chainConfig = api.backend.ChainConfig()
   692  		vmctx       = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   693  		canon       = true
   694  	)
   695  	// Check if there are any overrides: the caller may wish to enable a future
   696  	// fork when executing this block. Note, such overrides are only applicable to the
   697  	// actual specified block, not any preceding blocks that we have to go through
   698  	// in order to obtain the state.
   699  	// Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork`
   700  
   701  	if config != nil && config.Overrides != nil {
   702  		// Copy the config, to not screw up the main config
   703  		// Note: the Clique-part is _not_ deep copied
   704  		chainConfigCopy := new(params.ChainConfig)
   705  		*chainConfigCopy = *chainConfig
   706  		chainConfig = chainConfigCopy
   707  		if berlin := config.Config.Overrides.BerlinBlock; berlin != nil {
   708  			chainConfig.BerlinBlock = berlin
   709  			canon = false
   710  		}
   711  	}
   712  	vmctx.L1CostFunc = core.NewL1CostFunc(chainConfig, statedb)
   713  	for i, tx := range block.Transactions() {
   714  		// Prepare the trasaction for un-traced execution
   715  		var (
   716  			msg, _    = tx.AsMessage(signer, block.BaseFee())
   717  			txContext = core.NewEVMTxContext(msg)
   718  			vmConf    vm.Config
   719  			dump      *os.File
   720  			writer    *bufio.Writer
   721  			err       error
   722  		)
   723  		// If the transaction needs tracing, swap out the configs
   724  		if tx.Hash() == txHash || txHash == (common.Hash{}) {
   725  			// Generate a unique temporary file to dump it into
   726  			prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
   727  			if !canon {
   728  				prefix = fmt.Sprintf("%valt-", prefix)
   729  			}
   730  			dump, err = os.CreateTemp(os.TempDir(), prefix)
   731  			if err != nil {
   732  				return nil, err
   733  			}
   734  			dumps = append(dumps, dump.Name())
   735  
   736  			// Swap out the noop logger to the standard tracer
   737  			writer = bufio.NewWriter(dump)
   738  			vmConf = vm.Config{
   739  				Debug:                   true,
   740  				Tracer:                  logger.NewJSONLogger(&logConfig, writer),
   741  				EnablePreimageRecording: true,
   742  			}
   743  		}
   744  		// Execute the transaction and flush any traces to disk
   745  		vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
   746  		statedb.Prepare(tx.Hash(), i)
   747  		_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
   748  		if writer != nil {
   749  			writer.Flush()
   750  		}
   751  		if dump != nil {
   752  			dump.Close()
   753  			log.Info("Wrote standard trace", "file", dump.Name())
   754  		}
   755  		if err != nil {
   756  			return dumps, err
   757  		}
   758  		// Finalize the state so any modifications are written to the trie
   759  		// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
   760  		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
   761  
   762  		// If we've traced the transaction we were looking for, abort
   763  		if tx.Hash() == txHash {
   764  			break
   765  		}
   766  	}
   767  	return dumps, nil
   768  }
   769  
   770  // containsTx reports whether the transaction with a certain hash
   771  // is contained within the specified block.
   772  func containsTx(block *types.Block, hash common.Hash) bool {
   773  	for _, tx := range block.Transactions() {
   774  		if tx.Hash() == hash {
   775  			return true
   776  		}
   777  	}
   778  	return false
   779  }
   780  
   781  // TraceTransaction returns the structured logs created during the execution of EVM
   782  // and returns them as a JSON object.
   783  func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
   784  	_, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
   785  	if err != nil {
   786  		return nil, err
   787  	}
   788  	// It shouldn't happen in practice.
   789  	if blockNumber == 0 {
   790  		return nil, errors.New("genesis is not traceable")
   791  	}
   792  	reexec := defaultTraceReexec
   793  	if config != nil && config.Reexec != nil {
   794  		reexec = *config.Reexec
   795  	}
   796  	block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
   797  	if err != nil {
   798  		return nil, err
   799  	}
   800  	msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
   801  	if err != nil {
   802  		return nil, err
   803  	}
   804  	txctx := &Context{
   805  		BlockHash: blockHash,
   806  		TxIndex:   int(index),
   807  		TxHash:    hash,
   808  	}
   809  	return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
   810  }
   811  
   812  // TraceCall lets you trace a given eth_call. It collects the structured logs
   813  // created during the execution of EVM if the given transaction was added on
   814  // top of the provided block and returns them as a JSON object.
   815  func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
   816  	// Try to retrieve the specified block
   817  	var (
   818  		err   error
   819  		block *types.Block
   820  	)
   821  	if hash, ok := blockNrOrHash.Hash(); ok {
   822  		block, err = api.blockByHash(ctx, hash)
   823  	} else if number, ok := blockNrOrHash.Number(); ok {
   824  		if number == rpc.PendingBlockNumber {
   825  			// We don't have access to the miner here. For tracing 'future' transactions,
   826  			// it can be done with block- and state-overrides instead, which offers
   827  			// more flexibility and stability than trying to trace on 'pending', since
   828  			// the contents of 'pending' is unstable and probably not a true representation
   829  			// of what the next actual block is likely to contain.
   830  			return nil, errors.New("tracing on top of pending is not supported")
   831  		}
   832  		block, err = api.blockByNumber(ctx, number)
   833  	} else {
   834  		return nil, errors.New("invalid arguments; neither block nor hash specified")
   835  	}
   836  	if err != nil {
   837  		return nil, err
   838  	}
   839  	// try to recompute the state
   840  	reexec := defaultTraceReexec
   841  	if config != nil && config.Reexec != nil {
   842  		reexec = *config.Reexec
   843  	}
   844  	statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
   845  	if err != nil {
   846  		return nil, err
   847  	}
   848  	vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
   849  	// Apply the customization rules if required.
   850  	if config != nil {
   851  		if err := config.StateOverrides.Apply(statedb); err != nil {
   852  			return nil, err
   853  		}
   854  		config.BlockOverrides.Apply(&vmctx)
   855  	}
   856  	vmctx.L1CostFunc = core.NewL1CostFunc(api.backend.ChainConfig(), statedb)
   857  	// Execute the trace
   858  	msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
   859  	if err != nil {
   860  		return nil, err
   861  	}
   862  
   863  	var traceConfig *TraceConfig
   864  	if config != nil {
   865  		traceConfig = &TraceConfig{
   866  			Config:  config.Config,
   867  			Tracer:  config.Tracer,
   868  			Timeout: config.Timeout,
   869  			Reexec:  config.Reexec,
   870  		}
   871  	}
   872  	return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
   873  }
   874  
   875  // traceTx configures a new tracer according to the provided configuration, and
   876  // executes the given message in the provided environment. The return value will
   877  // be tracer dependent.
   878  func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
   879  	var (
   880  		tracer    Tracer
   881  		err       error
   882  		timeout   = defaultTraceTimeout
   883  		txContext = core.NewEVMTxContext(message)
   884  	)
   885  	if config == nil {
   886  		config = &TraceConfig{}
   887  	}
   888  	// Default tracer is the struct logger
   889  	tracer = logger.NewStructLogger(config.Config)
   890  	if config.Tracer != nil {
   891  		tracer, err = New(*config.Tracer, txctx)
   892  		if err != nil {
   893  			return nil, err
   894  		}
   895  	}
   896  	// Define a meaningful timeout of a single transaction trace
   897  	if config.Timeout != nil {
   898  		if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
   899  			return nil, err
   900  		}
   901  	}
   902  	deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
   903  	go func() {
   904  		<-deadlineCtx.Done()
   905  		if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
   906  			tracer.Stop(errors.New("execution timeout"))
   907  		}
   908  	}()
   909  	defer cancel()
   910  
   911  	// Run the transaction with tracing enabled.
   912  	vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
   913  	// Call Prepare to clear out the statedb access list
   914  	statedb.Prepare(txctx.TxHash, txctx.TxIndex)
   915  	if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())); err != nil {
   916  		return nil, fmt.Errorf("tracing failed: %w", err)
   917  	}
   918  	return tracer.GetResult()
   919  }
   920  
   921  // APIs return the collection of RPC services the tracer package offers.
   922  func APIs(backend Backend) []rpc.API {
   923  	// Append all the local APIs and return
   924  	return []rpc.API{
   925  		{
   926  			Namespace: "debug",
   927  			Service:   NewAPI(backend),
   928  		},
   929  	}
   930  }