github.com/klaytn/klaytn@v1.12.1/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  	"math/big"
    30  	"os"
    31  	"reflect"
    32  	"runtime"
    33  	"sync"
    34  	"sync/atomic"
    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  var (
    79  	HeavyAPIRequestLimit int32 = 500
    80  	heavyAPIRequestCount int32 = 0
    81  )
    82  
    83  // Backend interface provides the common API services with access to necessary functions.
    84  type Backend interface {
    85  	HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
    86  	HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
    87  	BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
    88  	BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
    89  	GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64)
    90  	RPCGasCap() *big.Int
    91  	ChainConfig() *params.ChainConfig
    92  	ChainDB() database.DBManager
    93  	Engine() consensus.Engine
    94  	// StateAtBlock returns the state corresponding to the stateroot of the block.
    95  	// N.B: For executing transactions on block N, the required stateRoot is block N-1,
    96  	// so this method should be called with the parent.
    97  	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error)
    98  	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.BlockContext, vm.TxContext, *state.StateDB, error)
    99  }
   100  
   101  // CommonAPI contains
   102  // - public methods that change behavior depending on `.unsafeTrace` flag.
   103  // For instance, TraceTransaction and TraceCall may or may not support custom tracers.
   104  // - private helper methods such as traceTx
   105  type CommonAPI struct {
   106  	backend     Backend
   107  	unsafeTrace bool
   108  }
   109  
   110  // API contains public methods that are considered "safe" to expose in public RPC.
   111  type API struct {
   112  	CommonAPI
   113  }
   114  
   115  // UnsafeAPI contains public methods that are considered "unsafe" to expose in public RPC.
   116  type UnsafeAPI struct {
   117  	CommonAPI
   118  }
   119  
   120  // NewUnsafeAPI creates a new UnsafeAPI definition
   121  func NewUnsafeAPI(backend Backend) *UnsafeAPI {
   122  	return &UnsafeAPI{
   123  		CommonAPI{backend: backend, unsafeTrace: true},
   124  	}
   125  }
   126  
   127  // NewAPI creates a new API definition
   128  func NewAPI(backend Backend) *API {
   129  	return &API{
   130  		CommonAPI{backend: backend, unsafeTrace: false},
   131  	}
   132  }
   133  
   134  type chainContext struct {
   135  	backend Backend
   136  	ctx     context.Context
   137  }
   138  
   139  func (context *chainContext) Engine() consensus.Engine {
   140  	return context.backend.Engine()
   141  }
   142  
   143  func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
   144  	header, err := context.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
   145  	if err != nil {
   146  		return nil
   147  	}
   148  	if header.Hash() == hash {
   149  		return header
   150  	}
   151  	header, err = context.backend.HeaderByHash(context.ctx, hash)
   152  	if err != nil {
   153  		return nil
   154  	}
   155  	return header
   156  }
   157  
   158  // chainContext constructs the context reader which is used by the evm for reading
   159  // the necessary chain context.
   160  func newChainContext(ctx context.Context, backend Backend) blockchain.ChainContext {
   161  	return &chainContext{backend: backend, ctx: ctx}
   162  }
   163  
   164  // blockByNumber is the wrapper of the chain access function offered by the backend.
   165  // It will return an error if the block is not found.
   166  func (api *CommonAPI) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
   167  	return api.backend.BlockByNumber(ctx, number)
   168  }
   169  
   170  // blockByHash is the wrapper of the chain access function offered by the backend.
   171  // It will return an error if the block is not found.
   172  func (api *CommonAPI) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
   173  	return api.backend.BlockByHash(ctx, hash)
   174  }
   175  
   176  // blockByNumberAndHash is the wrapper of the chain access function offered by
   177  // the backend. It will return an error if the block is not found.
   178  func (api *CommonAPI) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) {
   179  	block, err := api.blockByNumber(ctx, number)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	if block.Hash() == hash {
   184  		return block, nil
   185  	}
   186  	return api.blockByHash(ctx, hash)
   187  }
   188  
   189  // TraceConfig holds extra parameters to trace functions.
   190  type TraceConfig struct {
   191  	*vm.LogConfig
   192  	Tracer        *string
   193  	Timeout       *string
   194  	LoggerTimeout *string
   195  	Reexec        *uint64
   196  }
   197  
   198  // StdTraceConfig holds extra parameters to standard-json trace functions.
   199  type StdTraceConfig struct {
   200  	*vm.LogConfig
   201  	Reexec *uint64
   202  	TxHash common.Hash
   203  }
   204  
   205  // txTraceResult is the result of a single transaction trace.
   206  type txTraceResult struct {
   207  	TxHash common.Hash `json:"txHash,omitempty"` // transaction hash
   208  	Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
   209  	Error  string      `json:"error,omitempty"`  // Trace failure produced by the tracer
   210  }
   211  
   212  // blockTraceTask represents a single block trace task when an entire chain is
   213  // being traced.
   214  type blockTraceTask struct {
   215  	statedb *state.StateDB   // Intermediate state prepped for tracing
   216  	block   *types.Block     // Block to trace the transactions from
   217  	rootref common.Hash      // Trie root reference held for this task
   218  	results []*txTraceResult // Trace results procudes by the task
   219  }
   220  
   221  // blockTraceResult represets the results of tracing a single block when an entire
   222  // chain is being traced.
   223  type blockTraceResult struct {
   224  	Block  hexutil.Uint64   `json:"block"`  // Block number corresponding to this trace
   225  	Hash   common.Hash      `json:"hash"`   // Block hash corresponding to this trace
   226  	Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
   227  }
   228  
   229  // txTraceTask represents a single transaction trace task when an entire block
   230  // is being traced.
   231  type txTraceTask struct {
   232  	statedb *state.StateDB // Intermediate state prepped for tracing
   233  	index   int            // Transaction offset in the block
   234  }
   235  
   236  func checkRangeAndReturnBlock(api *CommonAPI, ctx context.Context, start, end rpc.BlockNumber) (*types.Block, *types.Block, error) {
   237  	// Fetch the block interval that we want to trace
   238  	from, err := api.blockByNumber(ctx, start)
   239  	if err != nil {
   240  		return nil, nil, err
   241  	}
   242  	to, err := api.blockByNumber(ctx, end)
   243  	if err != nil {
   244  		return nil, nil, err
   245  	}
   246  
   247  	// Trace the chain if we've found all our blocks
   248  	if from == nil {
   249  		return nil, nil, fmt.Errorf("starting block #%d not found", start)
   250  	}
   251  	if to == nil {
   252  		return nil, nil, fmt.Errorf("end block #%d not found", end)
   253  	}
   254  	if from.Number().Cmp(to.Number()) >= 0 {
   255  		return nil, nil, fmt.Errorf("end block #%d needs to come after start block #%d", end, start)
   256  	}
   257  	return from, to, nil
   258  }
   259  
   260  // TraceChain returns the structured logs created during the execution of EVM
   261  // between two blocks (excluding start) and returns them as a JSON object.
   262  func (api *UnsafeAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
   263  	from, to, err := checkRangeAndReturnBlock(&api.CommonAPI, ctx, start, end)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	// Tracing a chain is a **long** operation, only do with subscriptions
   268  	notifier, supported := rpc.NotifierFromContext(ctx)
   269  	if !supported {
   270  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
   271  	}
   272  	sub := notifier.CreateSubscription()
   273  	_, err = api.traceChain(from, to, config, notifier, sub)
   274  	return sub, err
   275  }
   276  
   277  // traceChain configures a new tracer according to the provided configuration, and
   278  // executes all the transactions contained within.
   279  // The traceChain operates in two modes: subscription mode and rpc mode
   280  //   - if notifier and sub is not nil, it works as a subscription mode and returns nothing
   281  //   - 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
   282  func (api *CommonAPI) traceChain(start, end *types.Block, config *TraceConfig, notifier *rpc.Notifier, sub *rpc.Subscription) (map[uint64]*blockTraceResult, error) {
   283  	// Prepare all the states for tracing. Note this procedure can take very
   284  	// long time. Timeout mechanism is necessary.
   285  	reexec := defaultTraceReexec
   286  	if config != nil && config.Reexec != nil {
   287  		reexec = *config.Reexec
   288  	}
   289  	// Execute all the transaction contained within the chain concurrently for each block
   290  	blocks := int(end.NumberU64() - start.NumberU64())
   291  	threads := runtime.NumCPU()
   292  	if threads > blocks {
   293  		threads = blocks
   294  	}
   295  	var (
   296  		pend     = new(sync.WaitGroup)
   297  		tasks    = make(chan *blockTraceTask, threads)
   298  		results  = make(chan *blockTraceTask, threads)
   299  		localctx = context.Background()
   300  	)
   301  	for th := 0; th < threads; th++ {
   302  		pend.Add(1)
   303  		go func() {
   304  			defer pend.Done()
   305  
   306  			// Fetch and execute the next block trace tasks
   307  			for task := range tasks {
   308  				signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
   309  
   310  				// Trace all the transactions contained within
   311  				for i, tx := range task.block.Transactions() {
   312  					msg, err := tx.AsMessageWithAccountKeyPicker(signer, task.statedb, task.block.NumberU64())
   313  					if err != nil {
   314  						logger.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   315  						task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
   316  						break
   317  					}
   318  
   319  					txCtx := blockchain.NewEVMTxContext(msg, task.block.Header())
   320  					blockCtx := blockchain.NewEVMBlockContext(task.block.Header(), newChainContext(localctx, api.backend), nil)
   321  
   322  					res, err := api.traceTx(localctx, msg, blockCtx, txCtx, task.statedb, config)
   323  					if err != nil {
   324  						task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
   325  						logger.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
   326  						break
   327  					}
   328  					task.statedb.Finalise(true, true)
   329  					task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
   330  				}
   331  				if notifier != nil {
   332  					// Stream the result back to the user or abort on teardown
   333  					select {
   334  					case results <- task:
   335  					case <-notifier.Closed():
   336  						return
   337  					}
   338  				} else {
   339  					results <- task
   340  				}
   341  			}
   342  		}()
   343  	}
   344  	// Start a goroutine to feed all the blocks into the tracers
   345  	begin := time.Now()
   346  
   347  	go func() {
   348  		var (
   349  			logged  time.Time
   350  			number  uint64
   351  			traced  uint64
   352  			failed  error
   353  			parent  common.Hash
   354  			statedb *state.StateDB
   355  		)
   356  		// Ensure everything is properly cleaned up on any exit path
   357  		defer func() {
   358  			close(tasks)
   359  			pend.Wait()
   360  
   361  			switch {
   362  			case failed != nil:
   363  				logger.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
   364  			case number < end.NumberU64():
   365  				logger.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin))
   366  			default:
   367  				logger.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
   368  			}
   369  			close(results)
   370  		}()
   371  		var preferDisk bool
   372  		// Feed all the blocks both into the tracer, as well as fast process concurrently
   373  		for number = start.NumberU64(); number < end.NumberU64(); number++ {
   374  			if notifier != nil {
   375  				// Stop tracing if interruption was requested
   376  				select {
   377  				case <-notifier.Closed():
   378  					return
   379  				default:
   380  				}
   381  			}
   382  			// Print progress logs if long enough time elapsed
   383  			if time.Since(logged) > log.StatsReportLimit {
   384  				logged = time.Now()
   385  				logger.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
   386  			}
   387  			// Retrieve the parent state to trace on top
   388  			block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
   389  			if err != nil {
   390  				failed = err
   391  				break
   392  			}
   393  			// Prepare the statedb for tracing. Don't use the live database for
   394  			// tracing to avoid persisting state junks into the database.
   395  			statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk)
   396  			if err != nil {
   397  				failed = err
   398  				break
   399  			}
   400  			if trieDb := statedb.Database().TrieDB(); trieDb != nil {
   401  				// Hold the reference for tracer, will be released at the final stage
   402  				trieDb.ReferenceRoot(block.Root())
   403  
   404  				// Release the parent state because it's already held by the tracer
   405  				if !common.EmptyHash(parent) {
   406  					trieDb.Dereference(parent)
   407  				}
   408  				// Prefer disk if the trie db memory grows too much
   409  				s1, s2, s3 := trieDb.Size()
   410  				if !preferDisk && (s1+s2+s3) > defaultTracechainMemLimit {
   411  					logger.Info("Switching to prefer-disk mode for tracing", "size", s1+s2+s3)
   412  					preferDisk = true
   413  				}
   414  			}
   415  			parent = block.Root()
   416  
   417  			next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
   418  			if err != nil {
   419  				failed = err
   420  				break
   421  			}
   422  			// Send the block over to the concurrent tracers (if not in the fast-forward phase)
   423  			txs := next.Transactions()
   424  			if notifier != nil {
   425  				select {
   426  				case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
   427  				case <-notifier.Closed():
   428  					return
   429  				}
   430  			} else {
   431  				tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}
   432  			}
   433  			traced += uint64(len(txs))
   434  		}
   435  	}()
   436  
   437  	waitForResult := func() map[uint64]*blockTraceResult {
   438  		// Keep reading the trace results and stream the to the user
   439  		var (
   440  			done = make(map[uint64]*blockTraceResult)
   441  			next = start.NumberU64() + 1
   442  		)
   443  		for res := range results {
   444  			// Queue up next received result
   445  			result := &blockTraceResult{
   446  				Block:  hexutil.Uint64(res.block.NumberU64()),
   447  				Hash:   res.block.Hash(),
   448  				Traces: res.results,
   449  			}
   450  			done[uint64(result.Block)] = result
   451  
   452  			// Dereference any parent tries held in memory by this task
   453  			if res.statedb.Database().TrieDB() != nil {
   454  				res.statedb.Database().TrieDB().Dereference(res.rootref)
   455  			}
   456  			if notifier != nil {
   457  				// Stream completed traces to the user, aborting on the first error
   458  				for result, ok := done[next]; ok; result, ok = done[next] {
   459  					if len(result.Traces) > 0 || next == end.NumberU64() {
   460  						notifier.Notify(sub.ID, result)
   461  					}
   462  					delete(done, next)
   463  					next++
   464  				}
   465  			} else {
   466  				if len(done) == blocks {
   467  					return done
   468  				}
   469  			}
   470  		}
   471  		return nil
   472  	}
   473  
   474  	if notifier != nil {
   475  		go waitForResult()
   476  		return nil, nil
   477  	}
   478  
   479  	return waitForResult(), nil
   480  }
   481  
   482  // TraceBlockByNumber returns the structured logs created during the execution of
   483  // EVM and returns them as a JSON object.
   484  func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
   485  	block, err := api.blockByNumber(ctx, number)
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  	return api.traceBlock(ctx, block, config)
   490  }
   491  
   492  // TraceBlockByNumberRange returns the ranged blocks tracing results
   493  // TODO-tracer: limit the result by the size of the return
   494  func (api *UnsafeAPI) TraceBlockByNumberRange(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (map[uint64]*blockTraceResult, error) {
   495  	// When the block range is [start,end], the actual tracing block would be [start+1,end]
   496  	// this is the reason why we change the block range to [start-1, end] so that we can trace [start,end] blocks
   497  	from, to, err := checkRangeAndReturnBlock(&api.CommonAPI, ctx, start-1, end)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  	return api.traceChain(from, to, config, nil, nil)
   502  }
   503  
   504  // TraceBlockByHash returns the structured logs created during the execution of
   505  // EVM and returns them as a JSON object.
   506  func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   507  	block, err := api.blockByHash(ctx, hash)
   508  	if err != nil {
   509  		return nil, err
   510  	}
   511  	return api.traceBlock(ctx, block, config)
   512  }
   513  
   514  // TraceBlock returns the structured logs created during the execution of EVM
   515  // and returns them as a JSON object.
   516  func (api *CommonAPI) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
   517  	block := new(types.Block)
   518  	if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
   519  		return nil, fmt.Errorf("could not decode block: %v", err)
   520  	}
   521  	return api.traceBlock(ctx, block, config)
   522  }
   523  
   524  // TraceBlockFromFile returns the structured logs created during the execution of
   525  // EVM and returns them as a JSON object.
   526  func (api *UnsafeAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) {
   527  	blob, err := os.ReadFile(file)
   528  	if err != nil {
   529  		return nil, fmt.Errorf("could not read file: %v", err)
   530  	}
   531  	return api.TraceBlock(ctx, common.Hex2Bytes(string(blob)), config)
   532  }
   533  
   534  // TraceBadBlock returns the structured logs created during the execution of
   535  // EVM against a block pulled from the pool of bad ones and returns them as a JSON
   536  // object.
   537  func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
   538  	blocks, err := api.backend.ChainDB().ReadAllBadBlocks()
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  	for _, block := range blocks {
   543  		if block.Hash() == hash {
   544  			return api.traceBlock(ctx, block, config)
   545  		}
   546  	}
   547  	return nil, fmt.Errorf("bad block %#x not found", hash)
   548  }
   549  
   550  // StandardTraceBlockToFile dumps the structured logs created during the
   551  // execution of EVM to the local file system and returns a list of files
   552  // to the caller.
   553  func (api *UnsafeAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   554  	block, err := api.blockByHash(ctx, hash)
   555  	if err != nil {
   556  		return nil, fmt.Errorf("block %#x not found", hash)
   557  	}
   558  	return api.standardTraceBlockToFile(ctx, block, config)
   559  }
   560  
   561  // StandardTraceBadBlockToFile dumps the structured logs created during the
   562  // execution of EVM against a block pulled from the pool of bad ones to the
   563  // local file system and returns a list of files to the caller.
   564  func (api *UnsafeAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
   565  	blocks, err := api.backend.ChainDB().ReadAllBadBlocks()
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  	for _, block := range blocks {
   570  		if block.Hash() == hash {
   571  			return api.standardTraceBlockToFile(ctx, block, config)
   572  		}
   573  	}
   574  	return nil, fmt.Errorf("bad block %#x not found", hash)
   575  }
   576  
   577  // traceBlock configures a new tracer according to the provided configuration, and
   578  // executes all the transactions contained within. The return value will be one item
   579  // per transaction, dependent on the requestd tracer.
   580  func (api *CommonAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
   581  	if !api.unsafeTrace {
   582  		if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit {
   583  			return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit))
   584  		}
   585  		atomic.AddInt32(&heavyAPIRequestCount, 1)
   586  		defer atomic.AddInt32(&heavyAPIRequestCount, -1)
   587  	}
   588  	if block.NumberU64() == 0 {
   589  		return nil, errors.New("genesis is not traceable")
   590  	}
   591  	// Create the parent state database
   592  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	reexec := defaultTraceReexec
   597  	if config != nil && config.Reexec != nil {
   598  		reexec = *config.Reexec
   599  	}
   600  
   601  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  	// Execute all the transaction contained within the block concurrently
   606  	var (
   607  		signer  = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   608  		txs     = block.Transactions()
   609  		results = make([]*txTraceResult, len(txs))
   610  
   611  		pend = new(sync.WaitGroup)
   612  		jobs = make(chan *txTraceTask, len(txs))
   613  	)
   614  	threads := runtime.NumCPU()
   615  	if threads > len(txs) {
   616  		threads = len(txs)
   617  	}
   618  	for th := 0; th < threads; th++ {
   619  		pend.Add(1)
   620  		go func() {
   621  			defer pend.Done()
   622  
   623  			// Fetch and execute the next transaction trace tasks
   624  			for task := range jobs {
   625  				msg, err := txs[task.index].AsMessageWithAccountKeyPicker(signer, task.statedb, block.NumberU64())
   626  				if err != nil {
   627  					logger.Warn("Tracing failed", "tx idx", task.index, "block", block.NumberU64(), "err", err)
   628  					results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
   629  					continue
   630  				}
   631  
   632  				txCtx := blockchain.NewEVMTxContext(msg, block.Header())
   633  				blockCtx := blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
   634  				res, err := api.traceTx(ctx, msg, blockCtx, txCtx, task.statedb, config)
   635  				if err != nil {
   636  					results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
   637  					continue
   638  				}
   639  				results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Result: res}
   640  			}
   641  		}()
   642  	}
   643  	// Feed the transactions into the tracers and return
   644  	var failed error
   645  	for i, tx := range txs {
   646  		// Send the trace task over for execution
   647  		jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
   648  
   649  		// Generate the next state snapshot fast without tracing
   650  		msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64())
   651  		if err != nil {
   652  			logger.Warn("Tracing failed", "hash", tx.Hash(), "block", block.NumberU64(), "err", err)
   653  			failed = err
   654  			break
   655  		}
   656  
   657  		txCtx := blockchain.NewEVMTxContext(msg, block.Header())
   658  		blockCtx := blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
   659  		vmenv := vm.NewEVM(blockCtx, txCtx, statedb, api.backend.ChainConfig(), &vm.Config{})
   660  		if _, err = blockchain.ApplyMessage(vmenv, msg); err != nil {
   661  			failed = err
   662  			break
   663  		}
   664  		// Finalize the state so any modifications are written to the trie
   665  		statedb.Finalise(true, true)
   666  	}
   667  	close(jobs)
   668  	pend.Wait()
   669  
   670  	// If execution failed in between, abort
   671  	if failed != nil {
   672  		return nil, failed
   673  	}
   674  	return results, nil
   675  }
   676  
   677  // standardTraceBlockToFile configures a new tracer which uses standard JSON output,
   678  // and traces either a full block or an individual transaction. The return value will
   679  // be one filename per transaction traced.
   680  func (api *CommonAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
   681  	// If we're tracing a single transaction, make sure it's present
   682  	if config != nil && !common.EmptyHash(config.TxHash) {
   683  		if !containsTx(block, config.TxHash) {
   684  			return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
   685  		}
   686  	}
   687  	if block.NumberU64() == 0 {
   688  		return nil, errors.New("genesis is not traceable")
   689  	}
   690  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  	reexec := defaultTraceReexec
   695  	if config != nil && config.Reexec != nil {
   696  		reexec = *config.Reexec
   697  	}
   698  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
   699  	if err != nil {
   700  		return nil, err
   701  	}
   702  	// Retrieve the tracing configurations, or use default values
   703  	var (
   704  		logConfig vm.LogConfig
   705  		txHash    common.Hash
   706  	)
   707  	if config != nil {
   708  		if config.LogConfig != nil {
   709  			logConfig = *config.LogConfig
   710  		}
   711  		txHash = config.TxHash
   712  	}
   713  	logConfig.Debug = true
   714  
   715  	// Execute transaction, either tracing all or just the requested one
   716  	var (
   717  		signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
   718  		dumps  []string
   719  	)
   720  	for i, tx := range block.Transactions() {
   721  		// Prepare the transaction for un-traced execution
   722  		msg, err := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64())
   723  		if err != nil {
   724  			logger.Warn("Tracing failed", "hash", tx.Hash(), "block", block.NumberU64(), "err", err)
   725  			return nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
   726  		}
   727  
   728  		var (
   729  			txCtx    = blockchain.NewEVMTxContext(msg, block.Header())
   730  			blockCtx = blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
   731  
   732  			vmConf vm.Config
   733  			dump   *os.File
   734  		)
   735  
   736  		// If the transaction needs tracing, swap out the configs
   737  		if tx.Hash() == txHash || common.EmptyHash(txHash) {
   738  			// Generate a unique temporary file to dump it into
   739  			prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
   740  
   741  			dump, err = os.CreateTemp(os.TempDir(), prefix)
   742  			if err != nil {
   743  				return nil, err
   744  			}
   745  			dumps = append(dumps, dump.Name())
   746  
   747  			// Swap out the noop logger to the standard tracer
   748  			vmConf = vm.Config{
   749  				Debug:                   true,
   750  				Tracer:                  vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)),
   751  				EnablePreimageRecording: true,
   752  			}
   753  		}
   754  		// Execute the transaction and flush any traces to disk
   755  		vmenv := vm.NewEVM(blockCtx, txCtx, statedb, api.backend.ChainConfig(), &vmConf)
   756  		_, err = blockchain.ApplyMessage(vmenv, msg)
   757  
   758  		if dump != nil {
   759  			dump.Close()
   760  			logger.Info("Wrote standard trace", "file", dump.Name())
   761  		}
   762  		if err != nil {
   763  			return dumps, err
   764  		}
   765  		// Finalize the state so any modifications are written to the trie
   766  		statedb.Finalise(true, true)
   767  
   768  		// If we've traced the transaction we were looking for, abort
   769  		if tx.Hash() == txHash {
   770  			break
   771  		}
   772  	}
   773  	return dumps, nil
   774  }
   775  
   776  // containsTx reports whether the transaction with a certain hash
   777  // is contained within the specified block.
   778  func containsTx(block *types.Block, hash common.Hash) bool {
   779  	for _, tx := range block.Transactions() {
   780  		if tx.Hash() == hash {
   781  			return true
   782  		}
   783  	}
   784  	return false
   785  }
   786  
   787  // TraceTransaction returns the structured logs created during the execution of EVM
   788  // and returns them as a JSON object.
   789  func (api *CommonAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
   790  	if !api.unsafeTrace {
   791  		if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit {
   792  			return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit))
   793  		}
   794  		atomic.AddInt32(&heavyAPIRequestCount, 1)
   795  		defer atomic.AddInt32(&heavyAPIRequestCount, -1)
   796  	}
   797  	// Retrieve the transaction and assemble its EVM context
   798  	tx, blockHash, blockNumber, index := api.backend.GetTxAndLookupInfo(hash)
   799  	if tx == nil {
   800  		return nil, fmt.Errorf("transaction %#x not found", hash)
   801  	}
   802  	// It shouldn't happen in practice.
   803  	if blockNumber == 0 {
   804  		return nil, errors.New("genesis is not traceable")
   805  	}
   806  	reexec := defaultTraceReexec
   807  	if config != nil && config.Reexec != nil {
   808  		reexec = *config.Reexec
   809  	}
   810  	block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
   811  	if err != nil {
   812  		return nil, err
   813  	}
   814  	msg, blockCtx, txCtx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
   815  	if err != nil {
   816  		return nil, err
   817  	}
   818  	// Trace the transaction and return
   819  	return api.traceTx(ctx, msg, blockCtx, txCtx, statedb, config)
   820  }
   821  
   822  // TraceCall lets you trace a given klay_call. It collects the structured logs
   823  // created during the execution of EVM if the given transaction was added on
   824  // top of the provided block and returns them as a JSON object.
   825  func (api *CommonAPI) TraceCall(ctx context.Context, args klaytnapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) {
   826  	if !api.unsafeTrace {
   827  		if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit {
   828  			return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit))
   829  		}
   830  		atomic.AddInt32(&heavyAPIRequestCount, 1)
   831  		defer atomic.AddInt32(&heavyAPIRequestCount, -1)
   832  	}
   833  	// Try to retrieve the specified block
   834  	var (
   835  		err   error
   836  		block *types.Block
   837  	)
   838  	if hash, ok := blockNrOrHash.Hash(); ok {
   839  		block, err = api.blockByHash(ctx, hash)
   840  	} else if number, ok := blockNrOrHash.Number(); ok {
   841  		block, err = api.blockByNumber(ctx, number)
   842  	} else {
   843  		return nil, errors.New("invalid arguments; neither block nor hash specified")
   844  	}
   845  	if err != nil {
   846  		return nil, err
   847  	}
   848  	// try to recompute the state
   849  	reexec := defaultTraceReexec
   850  	if config != nil && config.Reexec != nil {
   851  		reexec = *config.Reexec
   852  	}
   853  	statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
   854  	if err != nil {
   855  		return nil, err
   856  	}
   857  
   858  	// Execute the trace
   859  	intrinsicGas, err := types.IntrinsicGas(args.InputData(), nil, args.To == nil, api.backend.ChainConfig().Rules(block.Number()))
   860  	if err != nil {
   861  		return nil, err
   862  	}
   863  	basefee := new(big.Int).SetUint64(params.ZeroBaseFee)
   864  	if block.Header().BaseFee != nil {
   865  		basefee = block.Header().BaseFee
   866  	}
   867  	gasCap := uint64(0)
   868  	if rpcGasCap := api.backend.RPCGasCap(); rpcGasCap != nil {
   869  		gasCap = rpcGasCap.Uint64()
   870  	}
   871  	msg, err := args.ToMessage(gasCap, basefee, intrinsicGas)
   872  	if err != nil {
   873  		return nil, err
   874  	}
   875  
   876  	// Add gas fee to sender for estimating gasLimit/computing cost or calling a function by insufficient balance sender.
   877  	statedb.AddBalance(msg.ValidatedSender(), new(big.Int).Mul(new(big.Int).SetUint64(msg.Gas()), basefee))
   878  
   879  	txCtx := blockchain.NewEVMTxContext(msg, block.Header())
   880  	blockCtx := blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
   881  
   882  	return api.traceTx(ctx, msg, blockCtx, txCtx, statedb, config)
   883  }
   884  
   885  // traceTx configures a new tracer according to the provided configuration, and
   886  // executes the given message in the provided environment. The return value will
   887  // be tracer dependent.
   888  func (api *CommonAPI) traceTx(ctx context.Context, message blockchain.Message, blockCtx vm.BlockContext, txCtx vm.TxContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
   889  	// Assemble the structured logger or the JavaScript tracer
   890  	var (
   891  		tracer vm.Tracer
   892  		err    error
   893  	)
   894  	switch {
   895  	case config != nil && config.Tracer != nil:
   896  		// Define a meaningful timeout of a single transaction trace
   897  		timeout := defaultTraceTimeout
   898  		if config.Timeout != nil {
   899  			if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
   900  				return nil, err
   901  			}
   902  		}
   903  
   904  		if *config.Tracer == fastCallTracer {
   905  			tracer = vm.NewInternalTxTracer()
   906  		} else {
   907  			// Construct the JavaScript tracer to execute with
   908  			if tracer, err = New(*config.Tracer, new(Context), api.unsafeTrace); err != nil {
   909  				return nil, err
   910  			}
   911  		}
   912  		// Handle timeouts and RPC cancellations
   913  		deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
   914  		go func() {
   915  			<-deadlineCtx.Done()
   916  			if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
   917  				switch t := tracer.(type) {
   918  				case *Tracer:
   919  					t.Stop(errors.New("execution timeout"))
   920  				case *vm.InternalTxTracer:
   921  					t.Stop(errors.New("execution timeout"))
   922  				default:
   923  					logger.Warn("unknown tracer type", "type", reflect.TypeOf(t).String())
   924  				}
   925  			}
   926  		}()
   927  		defer cancel()
   928  
   929  	case config == nil:
   930  		tracer = vm.NewStructLogger(nil)
   931  
   932  	default:
   933  		tracer = vm.NewStructLogger(config.LogConfig)
   934  	}
   935  	// Run the transaction with tracing enabled.
   936  	vmenv := vm.NewEVM(blockCtx, txCtx, statedb, api.backend.ChainConfig(), &vm.Config{Debug: true, Tracer: tracer})
   937  
   938  	ret, err := blockchain.ApplyMessage(vmenv, message)
   939  	if err != nil {
   940  		return nil, fmt.Errorf("tracing failed: %v", err)
   941  	}
   942  	// Depending on the tracer type, format and return the output
   943  	switch tracer := tracer.(type) {
   944  	case *vm.StructLogger:
   945  		loggerTimeout := defaultLoggerTimeout
   946  		if config != nil && config.LoggerTimeout != nil {
   947  			if loggerTimeout, err = time.ParseDuration(*config.LoggerTimeout); err != nil {
   948  				return nil, err
   949  			}
   950  		}
   951  		if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs()); err == nil {
   952  			return &klaytnapi.ExecutionResult{
   953  				Gas:         ret.UsedGas,
   954  				Failed:      ret.Failed(),
   955  				ReturnValue: fmt.Sprintf("%x", ret.Return()),
   956  				StructLogs:  logs,
   957  			}, nil
   958  		} else {
   959  			return nil, err
   960  		}
   961  
   962  	case *Tracer:
   963  		return tracer.GetResult()
   964  	case *vm.InternalTxTracer:
   965  		return tracer.GetResult()
   966  
   967  	default:
   968  		panic(fmt.Sprintf("bad tracer type %T", tracer))
   969  	}
   970  }