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