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