github.com/lmittmann/w3@v0.20.0/module/debug/trace.go (about)

     1  package debug
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/big"
     8  
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/core/vm"
    11  	"github.com/holiman/uint256"
    12  	"github.com/lmittmann/w3/internal/hexutil"
    13  	"github.com/lmittmann/w3/internal/module"
    14  	"github.com/lmittmann/w3/w3types"
    15  )
    16  
    17  // TraceCall requests the trace of the given message.
    18  func TraceCall(msg *w3types.Message, blockNumber *big.Int, config *TraceConfig) w3types.RPCCallerFactory[*Trace] {
    19  	if config == nil {
    20  		config = &TraceConfig{}
    21  	}
    22  	return module.NewFactory(
    23  		"debug_traceCall",
    24  		[]any{msg, module.BlockNumberArg(blockNumber), config},
    25  		module.WithArgsWrapper[*Trace](msgArgsWrapper),
    26  	)
    27  }
    28  
    29  // TraceTx requests the trace of the transaction with the given hash.
    30  func TraceTx(txHash common.Hash, config *TraceConfig) w3types.RPCCallerFactory[*Trace] {
    31  	if config == nil {
    32  		config = &TraceConfig{}
    33  	}
    34  	return module.NewFactory[*Trace](
    35  		"debug_traceTransaction",
    36  		[]any{txHash, config},
    37  	)
    38  }
    39  
    40  type TraceConfig struct {
    41  	Overrides      w3types.State           // Override account state
    42  	BlockOverrides *w3types.BlockOverrides // Override block state
    43  	EnableStack    bool                    // Enable stack capture
    44  	EnableMemory   bool                    // Enable memory capture
    45  	EnableStorage  bool                    // Enable storage capture
    46  	Limit          uint64                  // Maximum number of StructLog's to capture (all if zero)
    47  }
    48  
    49  // MarshalJSON implements the [json.Marshaler].
    50  func (c *TraceConfig) MarshalJSON() ([]byte, error) {
    51  	type config struct {
    52  		Overrides        w3types.State           `json:"stateOverrides,omitempty"`
    53  		BlockOverrides   *w3types.BlockOverrides `json:"blockOverrides,omitempty"`
    54  		DisableStorage   bool                    `json:"disableStorage,omitempty"`
    55  		DisableStack     bool                    `json:"disableStack,omitempty"`
    56  		EnableMemory     bool                    `json:"enableMemory,omitempty"`
    57  		EnableReturnData bool                    `json:"enableReturnData,omitempty"`
    58  		Limit            uint64                  `json:"limit,omitempty"`
    59  	}
    60  
    61  	return json.Marshal(config{
    62  		Overrides:        c.Overrides,
    63  		BlockOverrides:   c.BlockOverrides,
    64  		DisableStorage:   !c.EnableStorage,
    65  		DisableStack:     !c.EnableStack,
    66  		EnableMemory:     c.EnableMemory,
    67  		EnableReturnData: true,
    68  		Limit:            c.Limit,
    69  	})
    70  }
    71  
    72  type Trace struct {
    73  	Gas        uint64       `json:"gas"`
    74  	Failed     bool         `json:"failed"`
    75  	Output     []byte       `json:"returnValue"`
    76  	StructLogs []*StructLog `json:"structLogs"`
    77  }
    78  
    79  func (t *Trace) UnmarshalJSON(data []byte) error {
    80  	type trace struct {
    81  		Gas        uint64        `json:"gas"`
    82  		Failed     bool          `json:"failed"`
    83  		Output     hexutil.Bytes `json:"returnValue"`
    84  		StructLogs []*StructLog  `json:"structLogs"`
    85  	}
    86  
    87  	var dec trace
    88  	if err := json.Unmarshal(data, &dec); err != nil {
    89  		return err
    90  	}
    91  
    92  	t.Gas = dec.Gas
    93  	t.Failed = dec.Failed
    94  	t.Output = dec.Output
    95  	t.StructLogs = dec.StructLogs
    96  	return nil
    97  }
    98  
    99  type StructLog struct {
   100  	Pc      uint64
   101  	Depth   uint
   102  	Gas     uint64
   103  	GasCost uint
   104  	Op      vm.OpCode
   105  	Stack   []uint256.Int
   106  	Memory  []byte
   107  	Storage w3types.Storage
   108  }
   109  
   110  func (l *StructLog) UnmarshalJSON(data []byte) error {
   111  	type structLog struct {
   112  		Pc      uint64
   113  		Depth   uint
   114  		Gas     uint64
   115  		GasCost uint
   116  		Op      string
   117  		Stack   []uint256.Int
   118  		Memory  memory
   119  		Storage map[optionalPrefixedHash]optionalPrefixedHash
   120  	}
   121  
   122  	var dec structLog
   123  	if err := json.Unmarshal(data, &dec); err != nil {
   124  		return err
   125  	}
   126  
   127  	l.Pc = dec.Pc
   128  	l.Depth = dec.Depth
   129  	l.Gas = dec.Gas
   130  	l.GasCost = dec.GasCost
   131  	l.Op = vm.StringToOp(dec.Op)
   132  	l.Stack = dec.Stack
   133  	l.Memory = dec.Memory
   134  
   135  	if len(dec.Storage) > 0 {
   136  		l.Storage = make(w3types.Storage, len(dec.Storage))
   137  		for k, v := range dec.Storage {
   138  			l.Storage[(common.Hash)(k)] = (common.Hash)(v)
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  // optionalPrefixedHash is a helper for unmarshaling hashes with or without
   145  // "0x"-prefix.
   146  type optionalPrefixedHash common.Hash
   147  
   148  func (h *optionalPrefixedHash) UnmarshalText(data []byte) error {
   149  	if len(data) > 2 && data[0] == '0' && (data[1] == 'x' || data[1] == 'X') {
   150  		data = data[2:]
   151  	}
   152  
   153  	if len(data) != 2*common.HashLength {
   154  		return fmt.Errorf("hex string has length %d, want 64", len(data))
   155  	}
   156  
   157  	_, err := hex.Decode((*h)[:], data)
   158  	return err
   159  }
   160  
   161  type memory []byte
   162  
   163  func (m *memory) UnmarshalJSON(data []byte) error {
   164  	var dec []optionalPrefixedHash
   165  	if err := json.Unmarshal(data, &dec); err != nil {
   166  		return err
   167  	}
   168  
   169  	*m = make([]byte, 0, 32*len(dec))
   170  	for _, data := range dec {
   171  		*m = append(*m, data[:]...)
   172  	}
   173  	return nil
   174  }
   175  
   176  func msgArgsWrapper(slice []any) ([]any, error) {
   177  	msg := slice[0].(*w3types.Message)
   178  	if msg.Input != nil || msg.Func == nil {
   179  		return slice, nil
   180  	}
   181  
   182  	input, err := msg.Func.EncodeArgs(msg.Args...)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	msg.Input = input
   187  	slice[0] = msg
   188  	return slice, nil
   189  }