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