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 }