github.com/theQRL/go-zond@v0.1.1/zond/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/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/zond/tracers" 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 // Create inner call tracer with default configuration, don't forward 133 // the OnlyTopCall or WithLog to inner for now 134 tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil) 135 if err != nil { 136 return nil, err 137 } 138 t, ok := tracer.(*callTracer) 139 if !ok { 140 return nil, errors.New("internal error: embedded tracer has wrong type") 141 } 142 143 return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil 144 } 145 146 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 147 func (t *flatCallTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 148 t.tracer.CaptureStart(env, from, to, create, input, gas, value) 149 // Update list of precompiles based on current block 150 rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) 151 t.activePrecompiles = vm.ActivePrecompiles(rules) 152 } 153 154 // CaptureEnd is called after the call finishes to finalize the tracing. 155 func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { 156 t.tracer.CaptureEnd(output, gasUsed, err) 157 } 158 159 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 160 func (t *flatCallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 161 t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) 162 } 163 164 // CaptureFault implements the EVMLogger interface to trace an execution fault. 165 func (t *flatCallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { 166 t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) 167 } 168 169 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 170 func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 171 t.tracer.CaptureEnter(typ, from, to, input, gas, value) 172 173 // Child calls must have a value, even if it's zero. 174 // Practically speaking, only STATICCALL has nil value. Set it to zero. 175 if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil { 176 t.tracer.callstack[len(t.tracer.callstack)-1].Value = big.NewInt(0) 177 } 178 } 179 180 // CaptureExit is called when EVM exits a scope, even if the scope didn't 181 // execute any code. 182 func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 183 t.tracer.CaptureExit(output, gasUsed, err) 184 185 // Parity traces don't include CALL/STATICCALLs to precompiles. 186 // By default we remove them from the callstack. 187 if t.config.IncludePrecompiles { 188 return 189 } 190 var ( 191 // call has been nested in parent 192 parent = t.tracer.callstack[len(t.tracer.callstack)-1] 193 call = parent.Calls[len(parent.Calls)-1] 194 typ = call.Type 195 to = call.To 196 ) 197 if typ == vm.CALL || typ == vm.STATICCALL { 198 if t.isPrecompiled(*to) { 199 t.tracer.callstack[len(t.tracer.callstack)-1].Calls = parent.Calls[:len(parent.Calls)-1] 200 } 201 } 202 } 203 204 func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) { 205 t.tracer.CaptureTxStart(gasLimit) 206 } 207 208 func (t *flatCallTracer) CaptureTxEnd(restGas uint64) { 209 t.tracer.CaptureTxEnd(restGas) 210 } 211 212 // GetResult returns an empty json object. 213 func (t *flatCallTracer) GetResult() (json.RawMessage, error) { 214 if len(t.tracer.callstack) < 1 { 215 return nil, errors.New("invalid number of calls") 216 } 217 218 flat, err := flatFromNested(&t.tracer.callstack[0], []int{}, t.config.ConvertParityErrors, t.ctx) 219 if err != nil { 220 return nil, err 221 } 222 223 res, err := json.Marshal(flat) 224 if err != nil { 225 return nil, err 226 } 227 return res, t.reason 228 } 229 230 // Stop terminates execution of the tracer at the first opportune moment. 231 func (t *flatCallTracer) Stop(err error) { 232 t.tracer.Stop(err) 233 } 234 235 // isPrecompiled returns whether the addr is a precompile. 236 func (t *flatCallTracer) isPrecompiled(addr common.Address) bool { 237 for _, p := range t.activePrecompiles { 238 if p == addr { 239 return true 240 } 241 } 242 return false 243 } 244 245 func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) { 246 var frame *flatCallFrame 247 switch input.Type { 248 case vm.CREATE, vm.CREATE2: 249 frame = newFlatCreate(input) 250 case vm.SELFDESTRUCT: 251 frame = newFlatSelfdestruct(input) 252 case vm.CALL, vm.STATICCALL, vm.CALLCODE, vm.DELEGATECALL: 253 frame = newFlatCall(input) 254 default: 255 return nil, fmt.Errorf("unrecognized call frame type: %s", input.Type) 256 } 257 258 frame.TraceAddress = traceAddress 259 frame.Error = input.Error 260 frame.Subtraces = len(input.Calls) 261 fillCallFrameFromContext(frame, ctx) 262 if convertErrs { 263 convertErrorToParity(frame) 264 } 265 266 // Revert output contains useful information (revert reason). 267 // Otherwise discard result. 268 if input.Error != "" && input.Error != vm.ErrExecutionReverted.Error() { 269 frame.Result = nil 270 } 271 272 output = append(output, *frame) 273 if len(input.Calls) > 0 { 274 for i, childCall := range input.Calls { 275 childAddr := childTraceAddress(traceAddress, i) 276 childCallCopy := childCall 277 flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) 278 if err != nil { 279 return nil, err 280 } 281 output = append(output, flat...) 282 } 283 } 284 285 return output, nil 286 } 287 288 func newFlatCreate(input *callFrame) *flatCallFrame { 289 var ( 290 actionInit = input.Input[:] 291 resultCode = input.Output[:] 292 ) 293 294 return &flatCallFrame{ 295 Type: strings.ToLower(vm.CREATE.String()), 296 Action: flatCallAction{ 297 From: &input.From, 298 Gas: &input.Gas, 299 Value: input.Value, 300 Init: &actionInit, 301 }, 302 Result: &flatCallResult{ 303 GasUsed: &input.GasUsed, 304 Address: input.To, 305 Code: &resultCode, 306 }, 307 } 308 } 309 310 func newFlatCall(input *callFrame) *flatCallFrame { 311 var ( 312 actionInput = input.Input[:] 313 resultOutput = input.Output[:] 314 ) 315 316 return &flatCallFrame{ 317 Type: strings.ToLower(vm.CALL.String()), 318 Action: flatCallAction{ 319 From: &input.From, 320 To: input.To, 321 Gas: &input.Gas, 322 Value: input.Value, 323 CallType: strings.ToLower(input.Type.String()), 324 Input: &actionInput, 325 }, 326 Result: &flatCallResult{ 327 GasUsed: &input.GasUsed, 328 Output: &resultOutput, 329 }, 330 } 331 } 332 333 func newFlatSelfdestruct(input *callFrame) *flatCallFrame { 334 return &flatCallFrame{ 335 Type: "suicide", 336 Action: flatCallAction{ 337 SelfDestructed: &input.From, 338 Balance: input.Value, 339 RefundAddress: input.To, 340 }, 341 } 342 } 343 344 func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) { 345 if ctx == nil { 346 return 347 } 348 if ctx.BlockHash != (common.Hash{}) { 349 callFrame.BlockHash = &ctx.BlockHash 350 } 351 if ctx.BlockNumber != nil { 352 callFrame.BlockNumber = ctx.BlockNumber.Uint64() 353 } 354 if ctx.TxHash != (common.Hash{}) { 355 callFrame.TransactionHash = &ctx.TxHash 356 } 357 callFrame.TransactionPosition = uint64(ctx.TxIndex) 358 } 359 360 func convertErrorToParity(call *flatCallFrame) { 361 if call.Error == "" { 362 return 363 } 364 365 if parityError, ok := parityErrorMapping[call.Error]; ok { 366 call.Error = parityError 367 } else { 368 for gethError, parityError := range parityErrorMappingStartingWith { 369 if strings.HasPrefix(call.Error, gethError) { 370 call.Error = parityError 371 } 372 } 373 } 374 } 375 376 func childTraceAddress(a []int, i int) []int { 377 child := make([]int, 0, len(a)+1) 378 child = append(child, a...) 379 child = append(child, i) 380 return child 381 }