github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/state/runtime/instrumentation/tracers/native/call_flat.go (about) 1 // Copyright 2022 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 "fmt" 23 "math/big" 24 "strings" 25 26 "github.com/0xPolygon/supernets2-node/state/runtime/fakevm" 27 "github.com/0xPolygon/supernets2-node/state/runtime/instrumentation/tracers" 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 flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go 33 //go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go 34 35 func init() { 36 tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) 37 } 38 39 var parityErrorMapping = map[string]string{ 40 "contract creation code storage out of gas": "Out of gas", 41 "out of gas": "Out of gas", 42 "gas uint64 overflow": "Out of gas", 43 "max code size exceeded": "Out of gas", 44 "invalid jump destination": "Bad jump destination", 45 "execution reverted": "Reverted", 46 "return data out of bounds": "Out of bounds", 47 "stack limit reached 1024 (1023)": "Out of stack", 48 "precompiled failed": "Built-in failed", 49 "invalid input length": "Built-in failed", 50 } 51 52 var parityErrorMappingStartingWith = map[string]string{ 53 "invalid opcode:": "Bad instruction", 54 "stack underflow": "Stack underflow", 55 } 56 57 // flatCallFrame is a standalone callframe. 58 type flatCallFrame struct { 59 Action flatCallAction `json:"action"` 60 BlockHash *common.Hash `json:"blockHash"` 61 BlockNumber uint64 `json:"blockNumber"` 62 Error string `json:"error,omitempty"` 63 Result *flatCallResult `json:"result,omitempty"` 64 Subtraces int `json:"subtraces"` 65 TraceAddress []int `json:"traceAddress"` 66 TransactionHash *common.Hash `json:"transactionHash"` 67 TransactionPosition uint64 `json:"transactionPosition"` 68 Type string `json:"type"` 69 } 70 71 type flatCallAction struct { 72 Author *common.Address `json:"author,omitempty"` 73 RewardType string `json:"rewardType,omitempty"` 74 SelfDestructed *common.Address `json:"address,omitempty"` 75 Balance *big.Int `json:"balance,omitempty"` 76 CallType string `json:"callType,omitempty"` 77 CreationMethod string `json:"creationMethod,omitempty"` 78 From *common.Address `json:"from,omitempty"` 79 Gas *uint64 `json:"gas,omitempty"` 80 Init *[]byte `json:"init,omitempty"` 81 Input *[]byte `json:"input,omitempty"` 82 RefundAddress *common.Address `json:"refundAddress,omitempty"` 83 To *common.Address `json:"to,omitempty"` 84 Value *big.Int `json:"value,omitempty"` 85 } 86 87 type flatCallActionMarshaling struct { 88 Balance *hexutil.Big 89 Gas *hexutil.Uint64 90 Init *hexutil.Bytes 91 Input *hexutil.Bytes 92 Value *hexutil.Big 93 } 94 95 type flatCallResult struct { 96 Address *common.Address `json:"address,omitempty"` 97 Code *[]byte `json:"code,omitempty"` 98 GasUsed *uint64 `json:"gasUsed,omitempty"` 99 Output *[]byte `json:"output,omitempty"` 100 } 101 102 type flatCallResultMarshaling struct { 103 Code *hexutil.Bytes 104 GasUsed *hexutil.Uint64 105 Output *hexutil.Bytes 106 } 107 108 // flatCallTracer reports call frame information of a tx in a flat format, i.e. 109 // as opposed to the nested format of `callTracer`. 110 type flatCallTracer struct { 111 tracer *callTracer 112 config flatCallTracerConfig 113 ctx *tracers.Context // Holds tracer context data 114 reason error // Textual reason for the interruption 115 activePrecompiles []common.Address // Updated on CaptureStart based on given rules 116 } 117 118 type flatCallTracerConfig struct { 119 ConvertParityErrors bool `json:"convertParityErrors"` // If true, call tracer converts errors to parity format 120 IncludePrecompiles bool `json:"includePrecompiles"` // If true, call tracer includes calls to precompiled contracts 121 } 122 123 // newFlatCallTracer returns a new flatCallTracer. 124 func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { 125 var config flatCallTracerConfig 126 if cfg != nil { 127 if err := json.Unmarshal(cfg, &config); err != nil { 128 return nil, err 129 } 130 } 131 132 tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, cfg) 133 if err != nil { 134 return nil, err 135 } 136 t, ok := tracer.(*callTracer) 137 if !ok { 138 return nil, errors.New("internal error: embedded tracer has wrong type") 139 } 140 141 return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil 142 } 143 144 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 145 func (t *flatCallTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 146 t.tracer.CaptureStart(env, from, to, create, input, gas, value) 147 // Update list of precompiles based on current block 148 rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) 149 t.activePrecompiles = fakevm.ActivePrecompiles(rules) 150 } 151 152 // CaptureEnd is called after the call finishes to finalize the tracing. 153 func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { 154 t.tracer.CaptureEnd(output, gasUsed, err) 155 } 156 157 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 158 func (t *flatCallTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) { 159 t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) 160 } 161 162 // CaptureFault implements the EVMLogger interface to trace an execution fault. 163 func (t *flatCallTracer) CaptureFault(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, depth int, err error) { 164 t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) 165 } 166 167 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 168 func (t *flatCallTracer) CaptureEnter(typ fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 169 t.tracer.CaptureEnter(typ, from, to, input, gas, value) 170 171 // Child calls must have a value, even if it's zero. 172 // Practically speaking, only STATICCALL has nil value. Set it to zero. 173 if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil { 174 t.tracer.callstack[len(t.tracer.callstack)-1].Value = big.NewInt(0) 175 } 176 } 177 178 // CaptureExit is called when EVM exits a scope, even if the scope didn't 179 // execute any code. 180 func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 181 t.tracer.CaptureExit(output, gasUsed, err) 182 183 // Parity traces don't include CALL/STATICCALLs to precompiles. 184 // By default we remove them from the callstack. 185 if t.config.IncludePrecompiles { 186 return 187 } 188 var ( 189 // call has been nested in parent 190 parent = t.tracer.callstack[len(t.tracer.callstack)-1] 191 call = parent.Calls[len(parent.Calls)-1] 192 typ = call.Type 193 to = call.To 194 ) 195 if typ == fakevm.CALL || typ == fakevm.STATICCALL { 196 if t.isPrecompiled(*to) { 197 t.tracer.callstack[len(t.tracer.callstack)-1].Calls = parent.Calls[:len(parent.Calls)-1] 198 } 199 } 200 } 201 202 func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) { 203 t.tracer.CaptureTxStart(gasLimit) 204 } 205 206 func (t *flatCallTracer) CaptureTxEnd(restGas uint64) { 207 t.tracer.CaptureTxEnd(restGas) 208 } 209 210 // GetResult returns an empty json object. 211 func (t *flatCallTracer) GetResult() (json.RawMessage, error) { 212 if len(t.tracer.callstack) < 1 { 213 return nil, errors.New("invalid number of calls") 214 } 215 216 flat, err := flatFromNested(&t.tracer.callstack[0], []int{}, t.config.ConvertParityErrors, t.ctx) 217 if err != nil { 218 return nil, err 219 } 220 221 res, err := json.Marshal(flat) 222 if err != nil { 223 return nil, err 224 } 225 return res, t.reason 226 } 227 228 // Stop terminates execution of the tracer at the first opportune moment. 229 func (t *flatCallTracer) Stop(err error) { 230 t.tracer.Stop(err) 231 } 232 233 // isPrecompiled returns whether the addr is a precompile. 234 func (t *flatCallTracer) isPrecompiled(addr common.Address) bool { 235 for _, p := range t.activePrecompiles { 236 if p == addr { 237 return true 238 } 239 } 240 return false 241 } 242 243 func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) { 244 var frame *flatCallFrame 245 switch input.Type { 246 case fakevm.CREATE, fakevm.CREATE2: 247 frame = newFlatCreate(input) 248 case fakevm.SELFDESTRUCT: 249 frame = newFlatSuicide(input) 250 case fakevm.CALL, fakevm.STATICCALL, fakevm.CALLCODE, fakevm.DELEGATECALL: 251 frame = newFlatCall(input) 252 default: 253 return nil, fmt.Errorf("unrecognized call frame type: %s", input.Type) 254 } 255 256 frame.TraceAddress = traceAddress 257 frame.Error = input.Error 258 frame.Subtraces = len(input.Calls) 259 fillCallFrameFromContext(frame, ctx) 260 if convertErrs { 261 convertErrorToParity(frame) 262 } 263 264 // Revert output contains useful information (revert reason). 265 // Otherwise discard result. 266 if input.Error != "" && input.Error != fakevm.ErrExecutionReverted.Error() { 267 frame.Result = nil 268 } 269 270 output = append(output, *frame) 271 if len(input.Calls) > 0 { 272 for i, childCall := range input.Calls { 273 childAddr := childTraceAddress(traceAddress, i) 274 childCallCopy := childCall 275 flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) 276 if err != nil { 277 return nil, err 278 } 279 output = append(output, flat...) 280 } 281 } 282 283 return output, nil 284 } 285 286 func newFlatCreate(input *callFrame) *flatCallFrame { 287 var ( 288 actionInit = input.Input[:] 289 resultCode = input.Output[:] 290 ) 291 292 return &flatCallFrame{ 293 Type: strings.ToLower(fakevm.CREATE.String()), 294 Action: flatCallAction{ 295 From: &input.From, 296 Gas: &input.Gas, 297 Value: input.Value, 298 Init: &actionInit, 299 }, 300 Result: &flatCallResult{ 301 GasUsed: &input.GasUsed, 302 Address: input.To, 303 Code: &resultCode, 304 }, 305 } 306 } 307 308 func newFlatCall(input *callFrame) *flatCallFrame { 309 var ( 310 actionInput = input.Input[:] 311 resultOutput = input.Output[:] 312 ) 313 314 return &flatCallFrame{ 315 Type: strings.ToLower(fakevm.CALL.String()), 316 Action: flatCallAction{ 317 From: &input.From, 318 To: input.To, 319 Gas: &input.Gas, 320 Value: input.Value, 321 CallType: strings.ToLower(input.Type.String()), 322 Input: &actionInput, 323 }, 324 Result: &flatCallResult{ 325 GasUsed: &input.GasUsed, 326 Output: &resultOutput, 327 }, 328 } 329 } 330 331 func newFlatSuicide(input *callFrame) *flatCallFrame { 332 return &flatCallFrame{ 333 Type: "suicide", 334 Action: flatCallAction{ 335 SelfDestructed: &input.From, 336 Balance: input.Value, 337 RefundAddress: input.To, 338 }, 339 } 340 } 341 342 func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) { 343 if ctx == nil { 344 return 345 } 346 if ctx.BlockHash != (common.Hash{}) { 347 callFrame.BlockHash = &ctx.BlockHash 348 } 349 if ctx.BlockNumber != nil { 350 callFrame.BlockNumber = ctx.BlockNumber.Uint64() 351 } 352 if ctx.TxHash != (common.Hash{}) { 353 callFrame.TransactionHash = &ctx.TxHash 354 } 355 callFrame.TransactionPosition = uint64(ctx.TxIndex) 356 } 357 358 func convertErrorToParity(call *flatCallFrame) { 359 if call.Error == "" { 360 return 361 } 362 363 if parityError, ok := parityErrorMapping[call.Error]; ok { 364 call.Error = parityError 365 } else { 366 for gethError, parityError := range parityErrorMappingStartingWith { 367 if strings.HasPrefix(call.Error, gethError) { 368 call.Error = parityError 369 } 370 } 371 } 372 } 373 374 func childTraceAddress(a []int, i int) []int { 375 child := make([]int, 0, len(a)+1) 376 child = append(child, a...) 377 child = append(child, i) 378 return child 379 }