github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/eth/tracers/api_bor.go (about)

     1  package tracers
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/ethereum/go-ethereum/common"
     8  	"github.com/ethereum/go-ethereum/consensus/bor/statefull"
     9  	"github.com/ethereum/go-ethereum/core"
    10  	"github.com/ethereum/go-ethereum/core/types"
    11  	"github.com/ethereum/go-ethereum/core/vm"
    12  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    13  	"github.com/ethereum/go-ethereum/internal/ethapi"
    14  	"github.com/ethereum/go-ethereum/log"
    15  	"github.com/ethereum/go-ethereum/rpc"
    16  )
    17  
    18  type BlockTraceResult struct {
    19  	// Trace of each transaction executed
    20  	Transactions []*TxTraceResult `json:"transactions,omitempty"`
    21  
    22  	// Block that we are executing on the trace
    23  	Block interface{} `json:"block"`
    24  }
    25  
    26  type TxTraceResult struct {
    27  	// Trace results produced by the tracer
    28  	Result interface{} `json:"result,omitempty"`
    29  
    30  	// Trace failure produced by the tracer
    31  	Error string `json:"error,omitempty"`
    32  
    33  	// IntermediateHash of the execution if succeeds
    34  	IntermediateHash common.Hash `json:"intermediatehash"`
    35  }
    36  
    37  func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *TraceConfig) (*BlockTraceResult, error) {
    38  	if block.NumberU64() == 0 {
    39  		return nil, fmt.Errorf("genesis is not traceable")
    40  	}
    41  
    42  	res := &BlockTraceResult{
    43  		Block: block,
    44  	}
    45  
    46  	// block object cannot be converted to JSON since much of the fields are non-public
    47  	blockFields, err := ethapi.RPCMarshalBlock(block, true, true, api.backend.ChainConfig(), api.backend.ChainDb())
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	res.Block = blockFields
    53  
    54  	parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	reexec := defaultTraceReexec
    60  	if config != nil && config.Reexec != nil {
    61  		reexec = *config.Reexec
    62  	}
    63  
    64  	// TODO: discuss consequences of setting preferDisk false.
    65  	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	// Execute all the transaction contained within the block concurrently
    71  	var (
    72  		signer                = types.MakeSigner(api.backend.ChainConfig(), block.Number())
    73  		txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
    74  		deleteEmptyObjects    = api.backend.ChainConfig().IsEIP158(block.Number())
    75  	)
    76  
    77  	blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
    78  
    79  	traceTxn := func(indx int, tx *types.Transaction, borTx bool) *TxTraceResult {
    80  		message, _ := tx.AsMessage(signer, block.BaseFee())
    81  		txContext := core.NewEVMTxContext(message)
    82  
    83  		tracer := logger.NewStructLogger(config.Config)
    84  
    85  		// Run the transaction with tracing enabled.
    86  		vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
    87  
    88  		// Call Prepare to clear out the statedb access list
    89  		// Not sure if we need to do this
    90  		statedb.Prepare(tx.Hash(), indx)
    91  
    92  		var execRes *core.ExecutionResult
    93  
    94  		if borTx {
    95  			callmsg := prepareCallMessage(message)
    96  			execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
    97  		} else {
    98  			execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), nil)
    99  		}
   100  
   101  		if err != nil {
   102  			return &TxTraceResult{
   103  				Error: err.Error(),
   104  			}
   105  		}
   106  
   107  		returnVal := fmt.Sprintf("%x", execRes.Return())
   108  		if len(execRes.Revert()) > 0 {
   109  			returnVal = fmt.Sprintf("%x", execRes.Revert())
   110  		}
   111  
   112  		result := &ethapi.ExecutionResult{
   113  			Gas:         execRes.UsedGas,
   114  			Failed:      execRes.Failed(),
   115  			ReturnValue: returnVal,
   116  			StructLogs:  ethapi.FormatLogs(tracer.StructLogs()),
   117  		}
   118  		res := &TxTraceResult{
   119  			Result:           result,
   120  			IntermediateHash: statedb.IntermediateRoot(deleteEmptyObjects),
   121  		}
   122  
   123  		return res
   124  	}
   125  
   126  	for indx, tx := range txs {
   127  		if stateSyncPresent && indx == len(txs)-1 {
   128  			res.Transactions = append(res.Transactions, traceTxn(indx, tx, true))
   129  		} else {
   130  			res.Transactions = append(res.Transactions, traceTxn(indx, tx, false))
   131  		}
   132  	}
   133  
   134  	return res, nil
   135  }
   136  
   137  type TraceBlockRequest struct {
   138  	Number     int64
   139  	Hash       string
   140  	IsBadBlock bool
   141  	Config     *TraceConfig
   142  }
   143  
   144  // If you use context as first parameter this function gets exposed automaticall on rpc endpoint
   145  func (api *API) TraceBorBlock(req *TraceBlockRequest) (*BlockTraceResult, error) {
   146  	ctx := context.Background()
   147  
   148  	var blockNumber rpc.BlockNumber
   149  	if req.Number == -1 {
   150  		blockNumber = rpc.LatestBlockNumber
   151  	} else {
   152  		blockNumber = rpc.BlockNumber(req.Number)
   153  	}
   154  
   155  	log.Debug("Tracing Bor Block", "block number", blockNumber)
   156  
   157  	block, err := api.blockByNumber(ctx, blockNumber)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	return api.traceBorBlock(ctx, block, req.Config)
   163  }