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