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