github.com/carter-ya/go-ethereum@v0.0.0-20230628080049-d2309be3983b/eth/tracers/native/call.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 native 18 19 import ( 20 "encoding/json" 21 "errors" 22 "math/big" 23 "sync/atomic" 24 "time" 25 26 "github.com/ethereum/go-ethereum/accounts/abi" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/common/hexutil" 29 "github.com/ethereum/go-ethereum/core/vm" 30 "github.com/ethereum/go-ethereum/eth/tracers" 31 ) 32 33 //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go 34 35 func init() { 36 register("callTracer", newCallTracer) 37 } 38 39 type callFrame struct { 40 Type vm.OpCode `json:"-"` 41 From common.Address `json:"from"` 42 Gas uint64 `json:"gas"` 43 GasUsed uint64 `json:"gasUsed"` 44 To common.Address `json:"to,omitempty" rlp:"optional"` 45 Input []byte `json:"input" rlp:"optional"` 46 Output []byte `json:"output,omitempty" rlp:"optional"` 47 Error string `json:"error,omitempty" rlp:"optional"` 48 Revertal string `json:"revertReason,omitempty"` 49 Calls []callFrame `json:"calls,omitempty" rlp:"optional"` 50 // Placed at end on purpose. The RLP will be decoded to 0 instead of 51 // nil if there are non-empty elements after in the struct. 52 Value *big.Int `json:"value,omitempty" rlp:"optional"` 53 } 54 55 func (f callFrame) TypeString() string { 56 return f.Type.String() 57 } 58 59 type callFrameMarshaling struct { 60 TypeString string `json:"type"` 61 Gas hexutil.Uint64 62 GasUsed hexutil.Uint64 63 Value *hexutil.Big 64 Input hexutil.Bytes 65 Output hexutil.Bytes 66 } 67 68 type callTracer struct { 69 env *vm.EVM 70 callstack []callFrame 71 config callTracerConfig 72 interrupt uint32 // Atomic flag to signal execution interruption 73 reason error // Textual reason for the interruption 74 } 75 76 type callTracerConfig struct { 77 OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls 78 } 79 80 // newCallTracer returns a native go tracer which tracks 81 // call frames of a tx, and implements vm.EVMLogger. 82 func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { 83 var config callTracerConfig 84 if cfg != nil { 85 if err := json.Unmarshal(cfg, &config); err != nil { 86 return nil, err 87 } 88 } 89 // First callframe contains tx context info 90 // and is populated on start and end. 91 return &callTracer{callstack: make([]callFrame, 1), config: config}, nil 92 } 93 94 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 95 func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 96 t.env = env 97 t.callstack[0] = callFrame{ 98 Type: vm.CALL, 99 From: from, 100 To: to, 101 Input: common.CopyBytes(input), 102 Gas: gas, 103 Value: value, 104 } 105 if create { 106 t.callstack[0].Type = vm.CREATE 107 } 108 } 109 110 // CaptureEnd is called after the call finishes to finalize the tracing. 111 func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { 112 t.callstack[0].GasUsed = gasUsed 113 output = common.CopyBytes(output) 114 if err == nil { 115 t.callstack[0].Output = output 116 return 117 } 118 t.callstack[0].Error = err.Error() 119 if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 { 120 return 121 } 122 t.callstack[0].Output = output 123 if len(output) < 4 { 124 return 125 } 126 if unpacked, err := abi.UnpackRevert(output); err == nil { 127 t.callstack[0].Revertal = unpacked 128 } 129 } 130 131 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 132 func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 133 } 134 135 // CaptureFault implements the EVMLogger interface to trace an execution fault. 136 func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { 137 } 138 139 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 140 func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 141 if t.config.OnlyTopCall { 142 return 143 } 144 // Skip if tracing was interrupted 145 if atomic.LoadUint32(&t.interrupt) > 0 { 146 t.env.Cancel() 147 return 148 } 149 150 call := callFrame{ 151 Type: typ, 152 From: from, 153 To: to, 154 Input: common.CopyBytes(input), 155 Gas: gas, 156 Value: value, 157 } 158 t.callstack = append(t.callstack, call) 159 } 160 161 // CaptureExit is called when EVM exits a scope, even if the scope didn't 162 // execute any code. 163 func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 164 if t.config.OnlyTopCall { 165 return 166 } 167 size := len(t.callstack) 168 if size <= 1 { 169 return 170 } 171 // pop call 172 call := t.callstack[size-1] 173 t.callstack = t.callstack[:size-1] 174 size -= 1 175 176 call.GasUsed = gasUsed 177 if err == nil { 178 call.Output = common.CopyBytes(output) 179 } else { 180 call.Error = err.Error() 181 if call.Type == vm.CREATE || call.Type == vm.CREATE2 { 182 call.To = common.Address{} 183 } 184 } 185 t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) 186 } 187 188 func (*callTracer) CaptureTxStart(gasLimit uint64) {} 189 190 func (*callTracer) CaptureTxEnd(restGas uint64) {} 191 192 // GetResult returns the json-encoded nested list of call traces, and any 193 // error arising from the encoding or forceful termination (via `Stop`). 194 func (t *callTracer) GetResult() (json.RawMessage, error) { 195 if len(t.callstack) != 1 { 196 return nil, errors.New("incorrect number of top-level calls") 197 } 198 res, err := json.Marshal(t.callstack[0]) 199 if err != nil { 200 return nil, err 201 } 202 return json.RawMessage(res), t.reason 203 } 204 205 // Stop terminates execution of the tracer at the first opportune moment. 206 func (t *callTracer) Stop(err error) { 207 t.reason = err 208 atomic.StoreUint32(&t.interrupt, 1) 209 }