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