github.com/klaytn/klaytn@v1.10.2/node/cn/tracers/api.go (about)

     1  // Modifications Copyright 2022 The klaytn Authors
     2  // Copyright 2021 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from eth/tracers/api.go (2022/08/08).
    19  // Modified and improved for the klaytn development.
    20  
    21  package tracers
    22  
    23  import (
    24  	"bufio"
    25  	"bytes"
    26  	"context"
    27  	"errors"
    28  	"fmt"
    29  	"io/ioutil"
    30  	"math/big"
    31  	"os"
    32  	"reflect"
    33  	"runtime"
    34  	"sync"
    35  	"time"
    36  
    37  	klaytnapi "github.com/klaytn/klaytn/api"
    38  	"github.com/klaytn/klaytn/blockchain"
    39  	"github.com/klaytn/klaytn/blockchain/state"
    40  	"github.com/klaytn/klaytn/blockchain/types"
    41  	"github.com/klaytn/klaytn/blockchain/vm"
    42  	"github.com/klaytn/klaytn/common"
    43  	"github.com/klaytn/klaytn/common/hexutil"
    44  	"github.com/klaytn/klaytn/consensus"
    45  	"github.com/klaytn/klaytn/log"
    46  	"github.com/klaytn/klaytn/networks/rpc"
    47  	"github.com/klaytn/klaytn/params"
    48  	"github.com/klaytn/klaytn/rlp"
    49  	"github.com/klaytn/klaytn/storage/database"
    50  )
    51  
    52  const (
    53  	// defaultTraceTimeout is the amount of time a single transaction can execute
    54  	// by default before being forcefully aborted.
    55  	defaultTraceTimeout = 5 * time.Second
    56  
    57  	// defaultLoggerTimeout is the amount of time a logger can aggregate trace logs
    58  	defaultLoggerTimeout = 1 * time.Second
    59  
    60  	// defaultTraceReexec is the number of blocks the tracer is willing to go back
    61  	// and reexecute to produce missing historical state necessary to run a specific
    62  	// trace.
    63  	defaultTraceReexec = uint64(128)
    64  
    65  	// defaultTracechainMemLimit is the size of the triedb, at which traceChain
    66  	// switches over and tries to use a disk-backed database instead of building
    67  	// on top of memory.
    68  	// For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
    69  	// will only be found every ~15K blocks or so.
    70  	// For klaytn, this value is set to a value 4 times larger compared to the ethereum setting.
    71  	defaultTracechainMemLimit = common.StorageSize(4 * 500 * 1024 * 1024)
    72  
    73  	// fastCallTracer is the go-version callTracer which is lighter and faster than
    74  	// Javascript version.
    75  	fastCallTracer = "fastCallTracer"
    76  )
    77  
    78  // Backend interface provides the common API services with access to necessary functions.
    79  type Backend interface {
    80  	HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
    81  	HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
    82  	BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
    83  	BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
    84  	GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64)
    85  	RPCGasCap() *big.Int
    86  	ChainConfig() *params.ChainConfig
    87  	ChainDB() database.DBManager
    88  	Engine() consensus.Engine
    89  	// StateAtBlock returns the state corresponding to the stateroot of the block.
    90  	// N.B: For executing transactions on block N, the required stateRoot is block N-1,
    91  	// so this method should be called with the parent.
    92  	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error)
    93  	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.Context, *state.StateDB, error)
    94  }
    95  
    96  // API is the collection of tracing APIs exposed over the private debugging endpoint.
    97  type API struct {
    98  	backend     Backend
    99  	unsafeTrace bool
   100  }
   101  
   102  // NewAPIUnsafeDisabled creates a new API definition for the tracing methods of the CN service,
   103  // only allowing predefined tracers.
   104  func NewAPIUnsafeDisabled(backend Backend) *API {
   105  	return &API{backend: backend, unsafeTrace: false}
   106  }
   107  
   108  // NewAPI creates a new API definition for the tracing methods of the CN service,
   109  // allowing both predefined tracers and Javascript snippet based tracing.
   110  func NewAPI(backend Backend) *API {
   111  	return &API{backend: backend, unsafeTrace: true}
   112  }
   113  
   114  type chainContext struct {
   115  	backend Backend
   116  	ctx     context.Context
   117  }
   118  
   119  func (context *chainContext) Engine() consensus.Engine {
   120  	return context.backend.Engine()
   121  }
   122  
   123  func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
   124  	header, err := context.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
   125  	if err != nil {
   126  		return nil
   127  	}
   128  	if header.Hash() == hash {
   129  		return header
   130  	}
   131  	header, err = context.backend.HeaderByHash(context.ctx, hash)
   132  	if err != nil {
   133  		return nil
   134  	}
   135  	return header
   136  }
   137  
   138  // chainContext constructs the context reader which is used by the evm for reading
   139  // the necessary chain context.
   140  func newChainContext(ctx context.Context, backend Backend) blockchain.ChainContext {
   141  	return &chainContext{backend: backend, ctx: ctx}
   142  }
   143  
   144  // blockByNumber is the wrapper of the chain access function offered by the backend.
   145  // It will return an error if the block is not found.
   146  func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
   147  	return api.backend.BlockByNumber(ctx, number)
   148  }
   149  
   150  // blockByHash is the wrapper of the chain access function offered by the backend.
   151  // It will return an error if the block is not found.
   152  func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
   153  	return api.backend.BlockByHash(ctx, hash)
   154  }
   155  
   156  // blockByNumberAndHash is the wrapper of the chain access function offered by
   157  // the backend. It will return an error if the block is not found.
   158  func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) {
   159  	block, err := api.blockByNumber(ctx, number)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	if block.Hash() == hash {
   164  		return block, nil
   165  	}
   166  	return api.blockByHash(ctx, hash)
   167  }
   168  
   169  // TraceConfig holds extra parameters to trace functions.
   170  type TraceConfig struct {
   171  	*vm.LogConfig
   172  	Tracer        *string
   173  	Timeout       *string
   174  	LoggerTimeout *string
   175  	Reexec        *uint64
   176  }
   177  
   178  // StdTraceConfig holds extra parameters to standard-json trace functions.
   179  type StdTraceConfig struct {
   180  	*vm.LogConfig
   181  	Reexec *uint64
   182  	TxHash common.Hash
   183  }
   184  
   185  // txTraceResult is the result of a single transaction trace.
   186  type txTraceResult struct {
   187  	TxHash common.Hash `json:"txHash,omitempty"` // transaction hash
   188  	Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
   189  	Error  string      `json:"error,omitempty"`  // Trace failure produced by the tracer
   190  }
   191  
   192  // blockTraceTask represents a single block trace task when an entire chain is
   193  // being traced.
   194  type blockTraceTask struct {
   195  	statedb *state.StateDB   // Intermediate state prepped for tracing
   196  	block   *types.Block     // Block to trace the transactions from
   197  	rootref common.Hash      // Trie root reference held for this task
   198  	results []*txTraceResult // Trace results procudes by the task
   199  }
   200  
   201  // blockTraceResult represets the results of tracing a single block when an entire
   202  // chain is being traced.
   203  type blockTraceResult struct {
   204  	Block  hexutil.Uint64   `json:"block"`  // Block number corresponding to this trace
   205  	Hash   common.Hash      `json:"hash"`   // Block hash corresponding to this trace
   206  	Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
   207  }
   208  
   209  // txTraceTask represents a single transaction trace task when an entire block
   210  // is being traced.
   211  type txTraceTask struct {
   212  	statedb *state.StateDB // Intermediate state prepped for tracing
   213  	index   int            // Transaction offset in the block
   214  }
   215  
   216  func checkRangeAndReturnBlock(api *API, ctx context.Context, start, end rpc.BlockNumber) (*types.Block, *types.Block, error) {
   217  	// Fetch the block interval that we want to trace
   218  	from, err := api.blockByNumber(ctx, start)
   219  	if err != nil {
   220  		return nil, nil, err
   221  	}
   222  	to, err := api.blockByNumber(ctx, end)
   223  	if err != nil {
   224  		return nil, nil, err
   225  	}
   226  
   227  	// Trace the chain if we've found all our blocks
   228  	if from == nil {
   229  		return nil, nil, fmt.Errorf("starting block #%d not found", start)
   230  	}
   231  	if to == nil {
   232  		return nil, nil, fmt.Errorf("end block #%d not found", end)
   233  	}
   234  	if from.Number().Cmp(to.Number()) >= 0 {
   235  		return nil, nil, fmt.Errorf("end block #%d needs to come after start block #%d", end, start)
   236  	}
   237  	return from, to, nil
   238  }
   239  
   240  // TraceChain returns the structured logs created during the execution of EVM
   241  // between two blocks (excluding start) and returns them as a JSON object.
   242  func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
   243  	from, to, err := checkRangeAndReturnBlock(api, ctx, start, end)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	// Tracing a chain is a **long** operation, only do with subscriptions
   248  	notifier, supported := rpc.NotifierFromContext(ctx)
   249  	if !supported {
   250  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
   251  	}
   252  	var sub *rpc.Subscription
   253  	sub = notifier.CreateSubscription()
   254  	_, err = api.traceChain(from, to, config, notifier, sub)
   255  	return sub, err
   256  }
   257  
   258  // traceChain configures a new tracer according to the provided configuration, and
   259  // executes all the transactions contained within.
   260  // The traceChain operates in two modes: subscription mode and rpc mode
   261  //  - if notifier and sub is not nil, it works as a subscription mode and returns nothing
   262  //  - if those parameters are nil, it works as a rpc mode and returns the block trace results, so it can pass the result through rpc-call
   263  func (api *API) traceChain(start, end *types.Block, config *TraceConfig, notifier *rpc.Notifier, sub *rpc.Subscription) (map[uint64]*blockTraceResult, error) {
   264  	// Prepare all the states for tracing. Note this procedure can take very
   265  	// long time. Timeout mechanism is necessary.
   266  	reexec := defaultTraceReexec
   267  	if config != nil && config.Reexec != nil {
   268  		reexec = *config.Reexec
   269  	}
   270  	// Execute all the transaction contained within the chain concurrently for each block
   271  	blocks := int(end.NumberU64() - start.NumberU64())
   272  	threads := runtime.NumCPU()
   273  	if threads > blocks {
   274  		threads = blocks
   275  	}
   276  	var (
   277  		pend     = new(sync.WaitGroup)
   278  		tasks    = make(chan *blockTraceTask, threads)
   279  		results  = make(chan *blockTraceTask, threads)
   280  		localctx = context.Background()
   281  	)
   282  	for th := 0; th < threads; th++ {
   283  		pend.Add(1)
   284  		go func() {
   285  			defer pend.Done()
   286  
   287  			// Fetch and execute the next block trace tasks
   288  			for task := range tasks {
   289  				signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
   290  
   291  				// Trace all the transactions contained within
   292  				for i, tx := range task.block.Transactions() {
   293  					msg, err := tx.AsMessageWithAccountKeyPicker(signer, task.statedb, task.block.NumberU64())
   294  					if err != nil {
   295  						logger.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   296  						task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
   297  						break
   298  					}
   299  
   300  					vmctx := blockchain.NewEVMContext(msg, task.block.Header(), newChainContext(localctx, api.backend), nil)
   301  
   302  					res, err := api.traceTx(localctx, msg, vmctx, task.statedb, config)
   303  					if err != nil {
   304  						task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
   305  						logger.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   306  						break
   307  					}
   308  					task.statedb.Finalise(true, true)
   309  					task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
   310  				}
   311  				if notifier != nil {
   312  					// Stream the result back to the user or abort on teardown
   313  					select {
   314  					case results <- task:
   315  					case <-notifier.Closed():
   316  						return
   317  					}
   318  				} else {
   319  					results <- task
   320  				}
   321  			}
   322  		}()
   323  	}
   324  	// Start a goroutine to feed all the blocks into the tracers
   325  	begin := time.Now()
   326  
   327  	go func() {
   328  		var (
   329  			logged  time.Time
   330  			number  uint64
   331  			traced  uint64
   332  			failed  error
   333  			parent  common.Hash
   334  			statedb *state.StateDB
   335  		)
   336  		// Ensure everything is properly cleaned up on any exit path
   337  		defer func() {
   338  			close(tasks)
   339  			pend.Wait()
   340  
   341  			switch {
   342  			case failed != nil:
   343  				logger.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
   344  			case number < end.NumberU64():
   345  				logger.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin))
   346  			default:
   347  				logger.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
   348  			}
   349  			close(results)
   350  		}()
   351  		var preferDisk bool
   352  		// Feed all the blocks both into the tracer, as well as fast process concurrently
   353  		for number = start.NumberU64(); number < end.NumberU64(); number++ {
   354  			if notifier != nil {
   355  				// Stop tracing if interruption was requested
   356  				select {
   357  				case <-notifier.Closed():
   358  					return
   359  				default:
   360  				}
   361  			}
   362  			// Print progress logs if long enough time elapsed
   363  			if time.Since(logged) > log.StatsReportLimit {
   364  				logged = time.Now()
   365  				logger.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
   366  			}
   367  			// Retrieve the parent state to trace on top
   368  			block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
   369  			if err != nil {
   370  				failed = err
   371  				break
   372  			}
   373  			// Prepare the statedb for tracing. Don't use the live database for
   374  			// tracing to avoid persisting state junks into the database.
   375  			statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk)
   376  			if err != nil {
   377  				failed = err
   378  				break
   379  			}
   380  			if trieDb := statedb.Database().TrieDB(); trieDb != nil {
   381  				// Hold the reference for tracer, will be released at the final stage
   382  				trieDb.Reference(block.Root(), common.Hash{})
   383  
   384  				// Release the parent state because it's already held by the tracer
   385  				if !common.EmptyHash(parent) {
   386  					trieDb.Dereference(parent)
   387  				}
   388  				// Prefer disk if the trie db memory grows too much
   389  				s1, s2, s3 := trieDb.Size()
   390  				if !preferDisk && (s1+s2+s3) > defaultTracechainMemLimit {
   391  					logger.Info("Switching to prefer-disk mode for tracing", "size", s1+s2+s3)
   392  					preferDisk = true
   393  				}
   394  			}
   395  			parent = block.Root()
   396  
   397  			next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
   398  			if err != nil {
   399  				failed = err
   400  				break
   401  			}
   402  			// Send the block over to the concurrent tracers (if not in the fast-forward phase)
   403  			txs := next.Transactions()
   404  			if notifier != nil {
   405  				select {
   406  				case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
   407  				case <-notifier.Closed():
   408  					return
   409  				}
   410  			} else {
   411  				tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}
   412  			}
   413  			traced += uint64(len(txs))
   414  		}
   415  	}()
   416  
   417  	waitForResult := func() map[uint64]*blockTraceResult {
   418  		// Keep reading the trace results and stream the to the user
   419  		var (
   420  			done = make(map[uint64]*blockTraceResult)
   421  			next = start.NumberU64() + 1
   422  		)
   423  		for res := range results {
   424  			// Queue up next received result
   425  			result := &blockTraceResult{
   426  				Block:  hexutil.Uint64(res.block.NumberU64()),
   427  				Hash:   res.block.Hash(),
   428  				Traces: res.results,
   429  			}
   430  			done[uint64(result.Block)] = result
   431  
   432  			// Dereference any parent tries held in memory by this task
   433  			if res.statedb.Database().TrieDB() != nil {
   434  				res.statedb.Database().TrieDB().Dereference(res.rootref)
   435  			}
   436  			if notifier != nil {
   437  				// Stream completed traces to the user, aborting on the first error
   438  				for result, ok := done[next]; ok; result, ok = done[next] {
   439  					if len(result.Traces) > 0 || next == end.NumberU64() {
   440  						notifier.Notify(sub.ID, result)
   441  					}
   442  					delete(done, next)
   443  					next++
   444  				}
   445  			} else {
   446  				if len(done) == blocks {
   447  					return done
   448  				}
   449  			}
   450  		}
   451  		return nil
   452  	}
   453  
   454  	if notifier != nil {
   455  		go waitForResult()
   456  		return nil, nil
   457  	}
   458  
   459  	return waitForResult(), nil
   460  }
   461  
   462  // TraceBlockByNumber returns the structured logs created during the execution of
   463  // EVM and returns them as a JSON object.
   464  func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
   465  	block, err := api.blockByNumber(ctx, number)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	return api.traceBlock(ctx, block, config)
   470  }
   471  
   472  // TraceBlockByNumberRange returns the ranged blocks tracing results
   473  // TODO-tracer: limit the result by the size of the return
   474  func (api *API) TraceBlockByNumberRange(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (map[uint64]*blockTraceResult, error) {
   475  	// When the block range is [start,end], the actual tracing block would be [start+1,end]
   476  	// this is the reason why we change the block range to [start-1, end] so that we can trace [start,end] blocks
   477  	from, to, err := checkRangeAndReturnBlock(api, ctx, start-1, end)
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  	return api.traceChain(from, to, config, nil, nil)
   482  }
   483  
   484  // TraceBlockByHash returns the structured logs created during the execution of
   485  // EVM and returns them as a JSON object.
   486  func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   487  	block, err := api.blockByHash(ctx, hash)
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  	return api.traceBlock(ctx, block, config)
   492  }
   493  
   494  // TraceBlock returns the structured logs created during the execution of EVM
   495  // and returns them as a JSON object.
   496  func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
   497  	block := new(types.Block)
   498  	if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
   499  		return nil, fmt.Errorf("could not decode block: %v", err)
   500  	}
   501  	return api.traceBlock(ctx, block, config)
   502  }
   503  
   504  // TraceBlockFromFile returns the structured logs created during the execution of
   505  // EVM and returns them as a JSON object.
   506  func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) {
   507  	if !api.unsafeTrace {
   508  		return nil, errors.New("TraceBlockFromFile is disabled")
   509  	}
   510  	blob, err := ioutil.ReadFile(file)
   511  	if err != nil {
   512  		return nil, fmt.Errorf("could not read file: %v", err)
   513  	}
   514  	return api.TraceBlock(ctx, common.Hex2Bytes(string(blob)), config)
   515  }
   516  
   517  // TraceBadBlock returns the structured logs created during the execution of
   518  // EVM against a block pulled from the pool of bad ones and returns them as a JSON
   519  // object.
   520  func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   521  	blocks, err := api.backend.ChainDB().ReadAllBadBlocks()
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  	for _, block := range blocks {
   526  		if block.Hash() == hash {
   527  			return api.traceBlock(ctx, block, config)
   528  		}
   529  	}
   530  	return nil, fmt.Errorf("bad block %#x not found", hash)
   531  }
   532  
   533  // StandardTraceBlockToFile dumps the structured logs created during the
   534  // execution of EVM to the local file system and returns a list of files
   535  // to the caller.
   536  func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   537  	if !api.unsafeTrace {
   538  		return nil, errors.New("StandardTraceBlockToFile is disabled")
   539  	}
   540  	block, err := api.blockByHash(ctx, hash)
   541  	if err != nil {
   542  		return nil, fmt.Errorf("block %#x not found", hash)
   543  	}
   544  	return api.standardTraceBlockToFile(ctx, block, config)
   545  }
   546  
   547  // StandardTraceBadBlockToFile dumps the structured logs created during the
   548  // execution of EVM against a block pulled from the pool of bad ones to the
   549  // local file system and returns a list of files to the caller.
   550  func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   551  	if !api.unsafeTrace {
   552  		return nil, errors.New("StandardTraceBadBlockToFile is disabled")
   553  	}
   554  	blocks, err := api.backend.ChainDB().ReadAllBadBlocks()
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  	for _, block := range blocks {
   559  		if block.Hash() == hash {
   560  			return api.standardTraceBlockToFile(ctx, block, config)
   561  		}
   562  	}
   563  	return nil, fmt.Errorf("bad block %#x not found", hash)
   564  }
   565  
   566  // traceBlock configures a new tracer according to the provided configuration, and
   567  // executes all the transactions contained within. The return value will be one item
   568  // per transaction, dependent on the requestd tracer.
   569  func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
   570  	if block.NumberU64() == 0 {
   571  		return nil, errors.New("genesis is not traceable")
   572  	}
   573  	// Create the parent state database
   574  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   575  	if err != nil {
   576  		return nil, err
   577  	}
   578  	reexec := defaultTraceReexec
   579  	if config != nil && config.Reexec != nil {
   580  		reexec = *config.Reexec
   581  	}
   582  
   583  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  	// Execute all the transaction contained within the block concurrently
   588  	var (
   589  		signer  = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   590  		txs     = block.Transactions()
   591  		results = make([]*txTraceResult, len(txs))
   592  
   593  		pend = new(sync.WaitGroup)
   594  		jobs = make(chan *txTraceTask, len(txs))
   595  	)
   596  	threads := runtime.NumCPU()
   597  	if threads > len(txs) {
   598  		threads = len(txs)
   599  	}
   600  	for th := 0; th < threads; th++ {
   601  		pend.Add(1)
   602  		go func() {
   603  			defer pend.Done()
   604  
   605  			// Fetch and execute the next transaction trace tasks
   606  			for task := range jobs {
   607  				msg, err := txs[task.index].AsMessageWithAccountKeyPicker(signer, task.statedb, block.NumberU64())
   608  				if err != nil {
   609  					logger.Warn("Tracing failed", "tx idx", task.index, "block", block.NumberU64(), "err", err)
   610  					results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
   611  					continue
   612  				}
   613  
   614  				vmctx := blockchain.NewEVMContext(msg, block.Header(), newChainContext(ctx, api.backend), nil)
   615  				res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
   616  				if err != nil {
   617  					results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
   618  					continue
   619  				}
   620  				results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Result: res}
   621  			}
   622  		}()
   623  	}
   624  	// Feed the transactions into the tracers and return
   625  	var failed error
   626  	for i, tx := range txs {
   627  		// Send the trace task over for execution
   628  		jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
   629  
   630  		// Generate the next state snapshot fast without tracing
   631  		msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64())
   632  		if err != nil {
   633  			logger.Warn("Tracing failed", "hash", tx.Hash(), "block", block.NumberU64(), "err", err)
   634  			failed = err
   635  			break
   636  		}
   637  
   638  		vmctx := blockchain.NewEVMContext(msg, block.Header(), newChainContext(ctx, api.backend), nil)
   639  		vmenv := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), &vm.Config{UseOpcodeComputationCost: true})
   640  		if _, _, kerr := blockchain.ApplyMessage(vmenv, msg); kerr.ErrTxInvalid != nil {
   641  			failed = kerr.ErrTxInvalid
   642  			break
   643  		}
   644  		// Finalize the state so any modifications are written to the trie
   645  		statedb.Finalise(true, true)
   646  	}
   647  	close(jobs)
   648  	pend.Wait()
   649  
   650  	// If execution failed in between, abort
   651  	if failed != nil {
   652  		return nil, failed
   653  	}
   654  	return results, nil
   655  }
   656  
   657  // standardTraceBlockToFile configures a new tracer which uses standard JSON output,
   658  // and traces either a full block or an individual transaction. The return value will
   659  // be one filename per transaction traced.
   660  func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
   661  	// If we're tracing a single transaction, make sure it's present
   662  	if config != nil && !common.EmptyHash(config.TxHash) {
   663  		if !containsTx(block, config.TxHash) {
   664  			return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
   665  		}
   666  	}
   667  	if block.NumberU64() == 0 {
   668  		return nil, errors.New("genesis is not traceable")
   669  	}
   670  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   671  	if err != nil {
   672  		return nil, err
   673  	}
   674  	reexec := defaultTraceReexec
   675  	if config != nil && config.Reexec != nil {
   676  		reexec = *config.Reexec
   677  	}
   678  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   679  	if err != nil {
   680  		return nil, err
   681  	}
   682  	// Retrieve the tracing configurations, or use default values
   683  	var (
   684  		logConfig vm.LogConfig
   685  		txHash    common.Hash
   686  	)
   687  	if config != nil {
   688  		if config.LogConfig != nil {
   689  			logConfig = *config.LogConfig
   690  		}
   691  		txHash = config.TxHash
   692  	}
   693  	logConfig.Debug = true
   694  
   695  	// Execute transaction, either tracing all or just the requested one
   696  	var (
   697  		signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   698  		dumps  []string
   699  	)
   700  	for i, tx := range block.Transactions() {
   701  		// Prepare the transaction for un-traced execution
   702  		msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64())
   703  		if err != nil {
   704  			logger.Warn("Tracing failed", "hash", tx.Hash(), "block", block.NumberU64(), "err", err)
   705  			return nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
   706  		}
   707  
   708  		var (
   709  			vmctx = blockchain.NewEVMContext(msg, block.Header(), newChainContext(ctx, api.backend), nil)
   710  
   711  			vmConf vm.Config
   712  			dump   *os.File
   713  		)
   714  
   715  		// If the transaction needs tracing, swap out the configs
   716  		if tx.Hash() == txHash || common.EmptyHash(txHash) {
   717  			// Generate a unique temporary file to dump it into
   718  			prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
   719  
   720  			dump, err = ioutil.TempFile(os.TempDir(), prefix)
   721  			if err != nil {
   722  				return nil, err
   723  			}
   724  			dumps = append(dumps, dump.Name())
   725  
   726  			// Swap out the noop logger to the standard tracer
   727  			vmConf = vm.Config{
   728  				Debug:                    true,
   729  				Tracer:                   vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)),
   730  				EnablePreimageRecording:  true,
   731  				UseOpcodeComputationCost: true,
   732  			}
   733  		}
   734  		// Execute the transaction and flush any traces to disk
   735  		vmenv := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), &vmConf)
   736  		_, _, kerr := blockchain.ApplyMessage(vmenv, msg)
   737  
   738  		if dump != nil {
   739  			dump.Close()
   740  			logger.Info("Wrote standard trace", "file", dump.Name())
   741  		}
   742  		if kerr.ErrTxInvalid != nil {
   743  			return dumps, kerr.ErrTxInvalid
   744  		}
   745  		// Finalize the state so any modifications are written to the trie
   746  		statedb.Finalise(true, true)
   747  
   748  		// If we've traced the transaction we were looking for, abort
   749  		if tx.Hash() == txHash {
   750  			break
   751  		}
   752  	}
   753  	return dumps, nil
   754  }
   755  
   756  // containsTx reports whether the transaction with a certain hash
   757  // is contained within the specified block.
   758  func containsTx(block *types.Block, hash common.Hash) bool {
   759  	for _, tx := range block.Transactions() {
   760  		if tx.Hash() == hash {
   761  			return true
   762  		}
   763  	}
   764  	return false
   765  }
   766  
   767  // TraceTransaction returns the structured logs created during the execution of EVM
   768  // and returns them as a JSON object.
   769  func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
   770  	// Retrieve the transaction and assemble its EVM context
   771  	tx, blockHash, blockNumber, index := api.backend.GetTxAndLookupInfo(hash)
   772  	if tx == nil {
   773  		return nil, fmt.Errorf("transaction %#x not found", hash)
   774  	}
   775  	// It shouldn't happen in practice.
   776  	if blockNumber == 0 {
   777  		return nil, errors.New("genesis is not traceable")
   778  	}
   779  	reexec := defaultTraceReexec
   780  	if config != nil && config.Reexec != nil {
   781  		reexec = *config.Reexec
   782  	}
   783  	block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
   784  	if err != nil {
   785  		return nil, err
   786  	}
   787  	msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
   788  	if err != nil {
   789  		return nil, err
   790  	}
   791  	// Trace the transaction and return
   792  	return api.traceTx(ctx, msg, vmctx, statedb, config)
   793  }
   794  
   795  // traceTx configures a new tracer according to the provided configuration, and
   796  // executes the given message in the provided environment. The return value will
   797  // be tracer dependent.
   798  func (api *API) traceTx(ctx context.Context, message blockchain.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
   799  	// Assemble the structured logger or the JavaScript tracer
   800  	var (
   801  		tracer vm.Tracer
   802  		err    error
   803  	)
   804  	switch {
   805  	case config != nil && config.Tracer != nil:
   806  		// Define a meaningful timeout of a single transaction trace
   807  		timeout := defaultTraceTimeout
   808  		if config.Timeout != nil {
   809  			if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
   810  				return nil, err
   811  			}
   812  		}
   813  
   814  		if *config.Tracer == fastCallTracer {
   815  			tracer = vm.NewInternalTxTracer()
   816  		} else {
   817  			// Construct the JavaScript tracer to execute with
   818  			if tracer, err = New(*config.Tracer, api.unsafeTrace); err != nil {
   819  				return nil, err
   820  			}
   821  		}
   822  		// Handle timeouts and RPC cancellations
   823  		deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
   824  		go func() {
   825  			<-deadlineCtx.Done()
   826  			if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
   827  				switch t := tracer.(type) {
   828  				case *Tracer:
   829  					t.Stop(errors.New("execution timeout"))
   830  				case *vm.InternalTxTracer:
   831  					t.Stop(errors.New("execution timeout"))
   832  				default:
   833  					logger.Warn("unknown tracer type", "type", reflect.TypeOf(t).String())
   834  				}
   835  			}
   836  		}()
   837  		defer cancel()
   838  
   839  	case config == nil:
   840  		tracer = vm.NewStructLogger(nil)
   841  
   842  	default:
   843  		tracer = vm.NewStructLogger(config.LogConfig)
   844  	}
   845  	// Run the transaction with tracing enabled.
   846  	vmenv := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), &vm.Config{Debug: true, Tracer: tracer, UseOpcodeComputationCost: true})
   847  
   848  	ret, gas, kerr := blockchain.ApplyMessage(vmenv, message)
   849  	if kerr.ErrTxInvalid != nil {
   850  		return nil, fmt.Errorf("tracing failed: %v", kerr.ErrTxInvalid)
   851  	}
   852  	// Depending on the tracer type, format and return the output
   853  	switch tracer := tracer.(type) {
   854  	case *vm.StructLogger:
   855  		loggerTimeout := defaultLoggerTimeout
   856  		if config != nil && config.LoggerTimeout != nil {
   857  			if loggerTimeout, err = time.ParseDuration(*config.LoggerTimeout); err != nil {
   858  				return nil, err
   859  			}
   860  		}
   861  		if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs()); err == nil {
   862  			return &klaytnapi.ExecutionResult{
   863  				Gas:         gas,
   864  				Failed:      kerr.Status != types.ReceiptStatusSuccessful,
   865  				ReturnValue: fmt.Sprintf("%x", ret),
   866  				StructLogs:  logs,
   867  			}, nil
   868  		} else {
   869  			return nil, err
   870  		}
   871  
   872  	case *Tracer:
   873  		return tracer.GetResult()
   874  	case *vm.InternalTxTracer:
   875  		return tracer.GetResult()
   876  
   877  	default:
   878  		panic(fmt.Sprintf("bad tracer type %T", tracer))
   879  	}
   880  }