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  }