github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/tracers/native/call.go (about) 1 // (c) 2020-2021, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2021 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package native 28 29 import ( 30 "encoding/json" 31 "errors" 32 "math/big" 33 "strconv" 34 "strings" 35 "sync/atomic" 36 "time" 37 38 "github.com/MetalBlockchain/subnet-evm/core/vm" 39 "github.com/MetalBlockchain/subnet-evm/eth/tracers" 40 "github.com/ethereum/go-ethereum/common" 41 ) 42 43 func init() { 44 register("callTracer", newCallTracer) 45 } 46 47 type callFrame struct { 48 Type string `json:"type"` 49 From string `json:"from"` 50 To string `json:"to,omitempty"` 51 Value string `json:"value,omitempty"` 52 Gas string `json:"gas"` 53 GasUsed string `json:"gasUsed"` 54 Input string `json:"input"` 55 Output string `json:"output,omitempty"` 56 Error string `json:"error,omitempty"` 57 Calls []callFrame `json:"calls,omitempty"` 58 } 59 60 type callTracer struct { 61 env *vm.EVM 62 callstack []callFrame 63 config callTracerConfig 64 interrupt uint32 // Atomic flag to signal execution interruption 65 reason error // Textual reason for the interruption 66 } 67 68 type callTracerConfig struct { 69 OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls 70 } 71 72 // newCallTracer returns a native go tracer which tracks 73 // call frames of a tx, and implements vm.EVMLogger. 74 func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { 75 var config callTracerConfig 76 if cfg != nil { 77 if err := json.Unmarshal(cfg, &config); err != nil { 78 return nil, err 79 } 80 } 81 // First callframe contains tx context info 82 // and is populated on start and end. 83 return &callTracer{callstack: make([]callFrame, 1), config: config}, nil 84 } 85 86 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 87 func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 88 t.env = env 89 t.callstack[0] = callFrame{ 90 Type: "CALL", 91 From: addrToHex(from), 92 To: addrToHex(to), 93 Input: bytesToHex(input), 94 Gas: uintToHex(gas), 95 Value: bigToHex(value), 96 } 97 if create { 98 t.callstack[0].Type = "CREATE" 99 } 100 } 101 102 // CaptureEnd is called after the call finishes to finalize the tracing. 103 func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { 104 t.callstack[0].GasUsed = uintToHex(gasUsed) 105 if err != nil { 106 t.callstack[0].Error = err.Error() 107 if err.Error() == "execution reverted" && len(output) > 0 { 108 t.callstack[0].Output = bytesToHex(output) 109 } 110 } else { 111 t.callstack[0].Output = bytesToHex(output) 112 } 113 } 114 115 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 116 func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 117 } 118 119 // CaptureFault implements the EVMLogger interface to trace an execution fault. 120 func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { 121 } 122 123 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 124 func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 125 if t.config.OnlyTopCall { 126 return 127 } 128 // Skip if tracing was interrupted 129 if atomic.LoadUint32(&t.interrupt) > 0 { 130 t.env.Cancel() 131 return 132 } 133 134 call := callFrame{ 135 Type: typ.String(), 136 From: addrToHex(from), 137 To: addrToHex(to), 138 Input: bytesToHex(input), 139 Gas: uintToHex(gas), 140 Value: bigToHex(value), 141 } 142 t.callstack = append(t.callstack, call) 143 } 144 145 // CaptureExit is called when EVM exits a scope, even if the scope didn't 146 // execute any code. 147 func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 148 if t.config.OnlyTopCall { 149 return 150 } 151 size := len(t.callstack) 152 if size <= 1 { 153 return 154 } 155 // pop call 156 call := t.callstack[size-1] 157 t.callstack = t.callstack[:size-1] 158 size -= 1 159 160 call.GasUsed = uintToHex(gasUsed) 161 if err == nil { 162 call.Output = bytesToHex(output) 163 } else { 164 call.Error = err.Error() 165 if call.Type == "CREATE" || call.Type == "CREATE2" { 166 call.To = "" 167 } 168 } 169 t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) 170 } 171 172 func (*callTracer) CaptureTxStart(gasLimit uint64) {} 173 174 func (*callTracer) CaptureTxEnd(restGas uint64) {} 175 176 // GetResult returns the json-encoded nested list of call traces, and any 177 // error arising from the encoding or forceful termination (via `Stop`). 178 func (t *callTracer) GetResult() (json.RawMessage, error) { 179 if len(t.callstack) != 1 { 180 return nil, errors.New("incorrect number of top-level calls") 181 } 182 res, err := json.Marshal(t.callstack[0]) 183 if err != nil { 184 return nil, err 185 } 186 return json.RawMessage(res), t.reason 187 } 188 189 // Stop terminates execution of the tracer at the first opportune moment. 190 func (t *callTracer) Stop(err error) { 191 t.reason = err 192 atomic.StoreUint32(&t.interrupt, 1) 193 } 194 195 func bytesToHex(s []byte) string { 196 return "0x" + common.Bytes2Hex(s) 197 } 198 199 func bigToHex(n *big.Int) string { 200 if n == nil { 201 return "" 202 } 203 return "0x" + n.Text(16) 204 } 205 206 func uintToHex(n uint64) string { 207 return "0x" + strconv.FormatUint(n, 16) 208 } 209 210 func addrToHex(a common.Address) string { 211 return strings.ToLower(a.Hex()) 212 }