github.com/janotchain/janota@v0.0.0-20220824112012-93ea4c5dee78/internal/ethapi/tracer.go (about) 1 // Copyright 2016 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 ethapi 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/big" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/common/hexutil" 28 "github.com/ethereum/go-ethereum/core/vm" 29 "github.com/robertkrimen/otto" 30 ) 31 32 // fakeBig is used to provide an interface to Javascript for 'big.NewInt' 33 type fakeBig struct{} 34 35 // NewInt creates a new big.Int with the specified int64 value. 36 func (fb *fakeBig) NewInt(x int64) *big.Int { 37 return big.NewInt(x) 38 } 39 40 // OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it 41 // as an object, instead of a number. 42 type opCodeWrapper struct { 43 op vm.OpCode 44 } 45 46 // toNumber returns the ID of this opcode as an integer 47 func (ocw *opCodeWrapper) toNumber() int { 48 return int(ocw.op) 49 } 50 51 // toString returns the string representation of the opcode 52 func (ocw *opCodeWrapper) toString() string { 53 return ocw.op.String() 54 } 55 56 // isPush returns true if the op is a Push 57 func (ocw *opCodeWrapper) isPush() bool { 58 return ocw.op.IsPush() 59 } 60 61 // MarshalJSON serializes the opcode as JSON 62 func (ocw *opCodeWrapper) MarshalJSON() ([]byte, error) { 63 return json.Marshal(ocw.op.String()) 64 } 65 66 // toValue returns an otto.Value for the opCodeWrapper 67 func (ocw *opCodeWrapper) toValue(vm *otto.Otto) otto.Value { 68 value, _ := vm.ToValue(ocw) 69 obj := value.Object() 70 obj.Set("toNumber", ocw.toNumber) 71 obj.Set("toString", ocw.toString) 72 obj.Set("isPush", ocw.isPush) 73 return value 74 } 75 76 // memoryWrapper provides a JS wrapper around vm.Memory 77 type memoryWrapper struct { 78 memory *vm.Memory 79 } 80 81 // slice returns the requested range of memory as a byte slice 82 func (mw *memoryWrapper) slice(begin, end int64) []byte { 83 return mw.memory.Get(begin, end-begin) 84 } 85 86 // getUint returns the 32 bytes at the specified address interpreted 87 // as an unsigned integer 88 func (mw *memoryWrapper) getUint(addr int64) *big.Int { 89 ret := big.NewInt(0) 90 ret.SetBytes(mw.memory.GetPtr(addr, 32)) 91 return ret 92 } 93 94 // toValue returns an otto.Value for the memoryWrapper 95 func (mw *memoryWrapper) toValue(vm *otto.Otto) otto.Value { 96 value, _ := vm.ToValue(mw) 97 obj := value.Object() 98 obj.Set("slice", mw.slice) 99 obj.Set("getUint", mw.getUint) 100 return value 101 } 102 103 // stackWrapper provides a JS wrapper around vm.Stack 104 type stackWrapper struct { 105 stack *vm.Stack 106 } 107 108 // peek returns the nth-from-the-top element of the stack. 109 func (sw *stackWrapper) peek(idx int) *big.Int { 110 return sw.stack.Data()[len(sw.stack.Data())-idx-1] 111 } 112 113 // length returns the length of the stack 114 func (sw *stackWrapper) length() int { 115 return len(sw.stack.Data()) 116 } 117 118 // toValue returns an otto.Value for the stackWrapper 119 func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value { 120 value, _ := vm.ToValue(sw) 121 obj := value.Object() 122 obj.Set("peek", sw.peek) 123 obj.Set("length", sw.length) 124 return value 125 } 126 127 // dbWrapper provides a JS wrapper around vm.Database 128 type dbWrapper struct { 129 db vm.StateDB 130 } 131 132 // getBalance retrieves an account's balance 133 func (dw *dbWrapper) getBalance(addr []byte) *big.Int { 134 return dw.db.GetBalance(common.BytesToAddress(addr)) 135 } 136 137 // getNonce retrieves an account's nonce 138 func (dw *dbWrapper) getNonce(addr []byte) uint64 { 139 return dw.db.GetNonce(common.BytesToAddress(addr)) 140 } 141 142 // getCode retrieves an account's code 143 func (dw *dbWrapper) getCode(addr []byte) []byte { 144 return dw.db.GetCode(common.BytesToAddress(addr)) 145 } 146 147 // getState retrieves an account's state data for the given hash 148 func (dw *dbWrapper) getState(addr []byte, hash common.Hash) common.Hash { 149 return dw.db.GetState(common.BytesToAddress(addr), hash) 150 } 151 152 // exists returns true iff the account exists 153 func (dw *dbWrapper) exists(addr []byte) bool { 154 return dw.db.Exist(common.BytesToAddress(addr)) 155 } 156 157 // toValue returns an otto.Value for the dbWrapper 158 func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { 159 value, _ := vm.ToValue(dw) 160 obj := value.Object() 161 obj.Set("getBalance", dw.getBalance) 162 obj.Set("getNonce", dw.getNonce) 163 obj.Set("getCode", dw.getCode) 164 obj.Set("getState", dw.getState) 165 obj.Set("exists", dw.exists) 166 return value 167 } 168 169 // contractWrapper provides a JS wrapper around vm.Contract 170 type contractWrapper struct { 171 contract *vm.Contract 172 } 173 174 func (c *contractWrapper) caller() common.Address { 175 return c.contract.Caller() 176 } 177 178 func (c *contractWrapper) address() common.Address { 179 return c.contract.Address() 180 } 181 182 func (c *contractWrapper) value() *big.Int { 183 return c.contract.Value() 184 } 185 186 func (c *contractWrapper) calldata() []byte { 187 return c.contract.Input 188 } 189 190 func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value { 191 value, _ := vm.ToValue(c) 192 obj := value.Object() 193 obj.Set("caller", c.caller) 194 obj.Set("address", c.address) 195 obj.Set("value", c.value) 196 obj.Set("calldata", c.calldata) 197 return value 198 } 199 200 // JavascriptTracer provides an implementation of Tracer that evaluates a 201 // Javascript function for each VM execution step. 202 type JavascriptTracer struct { 203 vm *otto.Otto // Javascript VM instance 204 traceobj *otto.Object // User-supplied object to call 205 op *opCodeWrapper // Wrapper around the VM opcode 206 log map[string]interface{} // (Reusable) map for the `log` arg to `step` 207 logvalue otto.Value // JS view of `log` 208 memory *memoryWrapper // Wrapper around the VM memory 209 stack *stackWrapper // Wrapper around the VM stack 210 db *dbWrapper // Wrapper around the VM environment 211 dbvalue otto.Value // JS view of `db` 212 contract *contractWrapper // Wrapper around the contract object 213 err error // Error, if one has occurred 214 result interface{} // Final result to return to the user 215 } 216 217 // NewJavascriptTracer instantiates a new JavascriptTracer instance. 218 // code specifies a Javascript snippet, which must evaluate to an expression 219 // returning an object with 'step' and 'result' functions. 220 func NewJavascriptTracer(code string) (*JavascriptTracer, error) { 221 vm := otto.New() 222 vm.Interrupt = make(chan func(), 1) 223 224 // Set up builtins for this environment 225 vm.Set("big", &fakeBig{}) 226 vm.Set("toHex", hexutil.Encode) 227 228 jstracer, err := vm.Object("(" + code + ")") 229 if err != nil { 230 return nil, err 231 } 232 // Check the required functions exist 233 step, err := jstracer.Get("step") 234 if err != nil { 235 return nil, err 236 } 237 if !step.IsFunction() { 238 return nil, fmt.Errorf("Trace object must expose a function step()") 239 } 240 241 result, err := jstracer.Get("result") 242 if err != nil { 243 return nil, err 244 } 245 if !result.IsFunction() { 246 return nil, fmt.Errorf("Trace object must expose a function result()") 247 } 248 // Create the persistent log object 249 var ( 250 op = new(opCodeWrapper) 251 mem = new(memoryWrapper) 252 stack = new(stackWrapper) 253 db = new(dbWrapper) 254 contract = new(contractWrapper) 255 ) 256 log := map[string]interface{}{ 257 "op": op.toValue(vm), 258 "memory": mem.toValue(vm), 259 "stack": stack.toValue(vm), 260 "contract": contract.toValue(vm), 261 } 262 logvalue, _ := vm.ToValue(log) 263 264 return &JavascriptTracer{ 265 vm: vm, 266 traceobj: jstracer, 267 op: op, 268 log: log, 269 logvalue: logvalue, 270 memory: mem, 271 stack: stack, 272 db: db, 273 dbvalue: db.toValue(vm), 274 contract: contract, 275 err: nil, 276 }, nil 277 } 278 279 // Stop terminates execution of any JavaScript 280 func (jst *JavascriptTracer) Stop(err error) { 281 jst.vm.Interrupt <- func() { 282 panic(err) 283 } 284 } 285 286 // callSafely executes a method on a JS object, catching any panics and 287 // returning them as error objects. 288 func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) { 289 defer func() { 290 if caught := recover(); caught != nil { 291 switch caught := caught.(type) { 292 case error: 293 err = caught 294 case string: 295 err = errors.New(caught) 296 case fmt.Stringer: 297 err = errors.New(caught.String()) 298 default: 299 panic(caught) 300 } 301 } 302 }() 303 304 value, err := jst.traceobj.Call(method, argumentList...) 305 ret, _ = value.Export() 306 return ret, err 307 } 308 309 func wrapError(context string, err error) error { 310 var message string 311 switch err := err.(type) { 312 case *otto.Error: 313 message = err.String() 314 default: 315 message = err.Error() 316 } 317 return fmt.Errorf("%v in server-side tracer function '%v'", message, context) 318 } 319 320 // CaptureState implements the Tracer interface to trace a single step of VM execution 321 func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { 322 if jst.err == nil { 323 jst.op.op = op 324 jst.memory.memory = memory 325 jst.stack.stack = stack 326 jst.db.db = env.StateDB 327 jst.contract.contract = contract 328 329 jst.log["pc"] = pc 330 jst.log["gas"] = gas 331 jst.log["cost"] = cost 332 jst.log["depth"] = depth 333 jst.log["account"] = contract.Address() 334 335 delete(jst.log, "error") 336 if err != nil { 337 jst.log["error"] = err 338 } 339 _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) 340 if err != nil { 341 jst.err = wrapError("step", err) 342 } 343 } 344 return nil 345 } 346 347 // CaptureEnd is called after the call finishes 348 func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { 349 //TODO! @Arachnid please figure out of there's anything we can use this method for 350 return nil 351 } 352 353 // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error 354 func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { 355 if jst.err != nil { 356 return nil, jst.err 357 } 358 359 result, err = jst.callSafely("result") 360 if err != nil { 361 err = wrapError("result", err) 362 } 363 return 364 }