github.com/theQRL/go-zond@v0.1.1/zond/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 25 "github.com/theQRL/go-zond/accounts/abi" 26 "github.com/theQRL/go-zond/common" 27 "github.com/theQRL/go-zond/common/hexutil" 28 "github.com/theQRL/go-zond/core/vm" 29 "github.com/theQRL/go-zond/log" 30 "github.com/theQRL/go-zond/zond/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 tracers.DefaultDirectory.Register("callTracer", newCallTracer, false) 37 } 38 39 type callLog struct { 40 Address common.Address `json:"address"` 41 Topics []common.Hash `json:"topics"` 42 Data hexutil.Bytes `json:"data"` 43 } 44 45 type callFrame struct { 46 Type vm.OpCode `json:"-"` 47 From common.Address `json:"from"` 48 Gas uint64 `json:"gas"` 49 GasUsed uint64 `json:"gasUsed"` 50 To *common.Address `json:"to,omitempty" rlp:"optional"` 51 Input []byte `json:"input" rlp:"optional"` 52 Output []byte `json:"output,omitempty" rlp:"optional"` 53 Error string `json:"error,omitempty" rlp:"optional"` 54 RevertReason string `json:"revertReason,omitempty"` 55 Calls []callFrame `json:"calls,omitempty" rlp:"optional"` 56 Logs []callLog `json:"logs,omitempty" rlp:"optional"` 57 // Placed at end on purpose. The RLP will be decoded to 0 instead of 58 // nil if there are non-empty elements after in the struct. 59 Value *big.Int `json:"value,omitempty" rlp:"optional"` 60 } 61 62 func (f callFrame) TypeString() string { 63 return f.Type.String() 64 } 65 66 func (f callFrame) failed() bool { 67 return len(f.Error) > 0 68 } 69 70 func (f *callFrame) processOutput(output []byte, err error) { 71 output = common.CopyBytes(output) 72 if err == nil { 73 f.Output = output 74 return 75 } 76 f.Error = err.Error() 77 if f.Type == vm.CREATE || f.Type == vm.CREATE2 { 78 f.To = nil 79 } 80 if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 { 81 return 82 } 83 f.Output = output 84 if len(output) < 4 { 85 return 86 } 87 if unpacked, err := abi.UnpackRevert(output); err == nil { 88 f.RevertReason = unpacked 89 } 90 } 91 92 type callFrameMarshaling struct { 93 TypeString string `json:"type"` 94 Gas hexutil.Uint64 95 GasUsed hexutil.Uint64 96 Value *hexutil.Big 97 Input hexutil.Bytes 98 Output hexutil.Bytes 99 } 100 101 type callTracer struct { 102 noopTracer 103 callstack []callFrame 104 config callTracerConfig 105 gasLimit uint64 106 interrupt atomic.Bool // Atomic flag to signal execution interruption 107 reason error // Textual reason for the interruption 108 } 109 110 type callTracerConfig struct { 111 OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls 112 WithLog bool `json:"withLog"` // If true, call tracer will collect event logs 113 } 114 115 // newCallTracer returns a native go tracer which tracks 116 // call frames of a tx, and implements vm.EVMLogger. 117 func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { 118 var config callTracerConfig 119 if cfg != nil { 120 if err := json.Unmarshal(cfg, &config); err != nil { 121 return nil, err 122 } 123 } 124 // First callframe contains tx context info 125 // and is populated on start and end. 126 return &callTracer{callstack: make([]callFrame, 1), config: config}, nil 127 } 128 129 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 130 func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 131 toCopy := to 132 t.callstack[0] = callFrame{ 133 Type: vm.CALL, 134 From: from, 135 To: &toCopy, 136 Input: common.CopyBytes(input), 137 Gas: t.gasLimit, 138 Value: value, 139 } 140 if create { 141 t.callstack[0].Type = vm.CREATE 142 } 143 } 144 145 // CaptureEnd is called after the call finishes to finalize the tracing. 146 func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { 147 t.callstack[0].processOutput(output, err) 148 } 149 150 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 151 func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 152 // skip if the previous op caused an error 153 if err != nil { 154 return 155 } 156 // Only logs need to be captured via opcode processing 157 if !t.config.WithLog { 158 return 159 } 160 // Avoid processing nested calls when only caring about top call 161 if t.config.OnlyTopCall && depth > 0 { 162 return 163 } 164 // Skip if tracing was interrupted 165 if t.interrupt.Load() { 166 return 167 } 168 switch op { 169 case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4: 170 size := int(op - vm.LOG0) 171 172 stack := scope.Stack 173 stackData := stack.Data() 174 175 // Don't modify the stack 176 mStart := stackData[len(stackData)-1] 177 mSize := stackData[len(stackData)-2] 178 topics := make([]common.Hash, size) 179 for i := 0; i < size; i++ { 180 topic := stackData[len(stackData)-2-(i+1)] 181 topics[i] = common.Hash(topic.Bytes32()) 182 } 183 184 data, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(mStart.Uint64()), int64(mSize.Uint64())) 185 if err != nil { 186 // mSize was unrealistically large 187 log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "callTracer", "offset", mStart, "size", mSize) 188 return 189 } 190 191 log := callLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutil.Bytes(data)} 192 t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log) 193 } 194 } 195 196 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 197 func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 198 if t.config.OnlyTopCall { 199 return 200 } 201 // Skip if tracing was interrupted 202 if t.interrupt.Load() { 203 return 204 } 205 206 toCopy := to 207 call := callFrame{ 208 Type: typ, 209 From: from, 210 To: &toCopy, 211 Input: common.CopyBytes(input), 212 Gas: gas, 213 Value: value, 214 } 215 t.callstack = append(t.callstack, call) 216 } 217 218 // CaptureExit is called when EVM exits a scope, even if the scope didn't 219 // execute any code. 220 func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 221 if t.config.OnlyTopCall { 222 return 223 } 224 size := len(t.callstack) 225 if size <= 1 { 226 return 227 } 228 // pop call 229 call := t.callstack[size-1] 230 t.callstack = t.callstack[:size-1] 231 size -= 1 232 233 call.GasUsed = gasUsed 234 call.processOutput(output, err) 235 t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) 236 } 237 238 func (t *callTracer) CaptureTxStart(gasLimit uint64) { 239 t.gasLimit = gasLimit 240 } 241 242 func (t *callTracer) CaptureTxEnd(restGas uint64) { 243 t.callstack[0].GasUsed = t.gasLimit - restGas 244 if t.config.WithLog { 245 // Logs are not emitted when the call fails 246 clearFailedLogs(&t.callstack[0], false) 247 } 248 } 249 250 // GetResult returns the json-encoded nested list of call traces, and any 251 // error arising from the encoding or forceful termination (via `Stop`). 252 func (t *callTracer) GetResult() (json.RawMessage, error) { 253 if len(t.callstack) != 1 { 254 return nil, errors.New("incorrect number of top-level calls") 255 } 256 257 res, err := json.Marshal(t.callstack[0]) 258 if err != nil { 259 return nil, err 260 } 261 return json.RawMessage(res), t.reason 262 } 263 264 // Stop terminates execution of the tracer at the first opportune moment. 265 func (t *callTracer) Stop(err error) { 266 t.reason = err 267 t.interrupt.Store(true) 268 } 269 270 // clearFailedLogs clears the logs of a callframe and all its children 271 // in case of execution failure. 272 func clearFailedLogs(cf *callFrame, parentFailed bool) { 273 failed := cf.failed() || parentFailed 274 // Clear own logs 275 if failed { 276 cf.Logs = nil 277 } 278 for i := range cf.Calls { 279 clearFailedLogs(&cf.Calls[i], failed) 280 } 281 }