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 := ðapi.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 }