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  }