github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/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-wtc 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-wtc 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/wtc/go-wtc/common" 27 "github.com/wtc/go-wtc/common/hexutil" 28 "github.com/wtc/go-wtc/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 common.Address) *big.Int { 134 return dw.db.GetBalance(addr) 135 } 136 137 // getNonce retrieves an account's nonce 138 func (dw *dbWrapper) getNonce(addr common.Address) uint64 { 139 return dw.db.GetNonce(addr) 140 } 141 142 // getCode retrieves an account's code 143 func (dw *dbWrapper) getCode(addr common.Address) []byte { 144 return dw.db.GetCode(addr) 145 } 146 147 // getState retrieves an account's state data for the given hash 148 func (dw *dbWrapper) getState(addr common.Address, hash common.Hash) common.Hash { 149 return dw.db.GetState(addr, hash) 150 } 151 152 // exists returns true iff the account exists 153 func (dw *dbWrapper) exists(addr common.Address) bool { 154 return dw.db.Exist(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 log map[string]interface{} // (Reusable) map for the `log` arg to `step` 206 logvalue otto.Value // JS view of `log` 207 memory *memoryWrapper // Wrapper around the VM memory 208 memvalue otto.Value // JS view of `memory` 209 stack *stackWrapper // Wrapper around the VM stack 210 stackvalue otto.Value // JS view of `stack` 211 db *dbWrapper // Wrapper around the VM environment 212 dbvalue otto.Value // JS view of `db` 213 contract *contractWrapper // Wrapper around the contract object 214 contractvalue otto.Value // JS view of `contract` 215 err error // Error, if one has occurred 216 } 217 218 // NewJavascriptTracer instantiates a new JavascriptTracer instance. 219 // code specifies a Javascript snippet, which must evaluate to an expression 220 // returning an object with 'step' and 'result' functions. 221 func NewJavascriptTracer(code string) (*JavascriptTracer, error) { 222 vm := otto.New() 223 vm.Interrupt = make(chan func(), 1) 224 225 // Set up builtins for this environment 226 vm.Set("big", &fakeBig{}) 227 vm.Set("toHex", hexutil.Encode) 228 229 jstracer, err := vm.Object("(" + code + ")") 230 if err != nil { 231 return nil, err 232 } 233 234 // Check the required functions exist 235 step, err := jstracer.Get("step") 236 if err != nil { 237 return nil, err 238 } 239 if !step.IsFunction() { 240 return nil, fmt.Errorf("Trace object must expose a function step()") 241 } 242 243 result, err := jstracer.Get("result") 244 if err != nil { 245 return nil, err 246 } 247 if !result.IsFunction() { 248 return nil, fmt.Errorf("Trace object must expose a function result()") 249 } 250 251 // Create the persistent log object 252 log := make(map[string]interface{}) 253 logvalue, _ := vm.ToValue(log) 254 255 // Create persistent wrappers for memory and stack 256 mem := &memoryWrapper{} 257 stack := &stackWrapper{} 258 db := &dbWrapper{} 259 contract := &contractWrapper{} 260 261 return &JavascriptTracer{ 262 vm: vm, 263 traceobj: jstracer, 264 log: log, 265 logvalue: logvalue, 266 memory: mem, 267 memvalue: mem.toValue(vm), 268 stack: stack, 269 stackvalue: stack.toValue(vm), 270 db: db, 271 dbvalue: db.toValue(vm), 272 contract: contract, 273 contractvalue: contract.toValue(vm), 274 err: nil, 275 }, nil 276 } 277 278 // Stop terminates execution of any JavaScript 279 func (jst *JavascriptTracer) Stop(err error) { 280 jst.vm.Interrupt <- func() { 281 panic(err) 282 } 283 } 284 285 // callSafely executes a method on a JS object, catching any panics and 286 // returning them as error objects. 287 func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) { 288 defer func() { 289 if caught := recover(); caught != nil { 290 switch caught := caught.(type) { 291 case error: 292 err = caught 293 case string: 294 err = errors.New(caught) 295 case fmt.Stringer: 296 err = errors.New(caught.String()) 297 default: 298 panic(caught) 299 } 300 } 301 }() 302 303 value, err := jst.traceobj.Call(method, argumentList...) 304 ret, _ = value.Export() 305 return ret, err 306 } 307 308 func wrapError(context string, err error) error { 309 var message string 310 switch err := err.(type) { 311 case *otto.Error: 312 message = err.String() 313 default: 314 message = err.Error() 315 } 316 return fmt.Errorf("%v in server-side tracer function '%v'", message, context) 317 } 318 319 // CaptureState implements the Tracer interface to trace a single step of VM execution 320 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 { 321 if jst.err == nil { 322 jst.memory.memory = memory 323 jst.stack.stack = stack 324 jst.db.db = env.StateDB 325 jst.contract.contract = contract 326 327 ocw := &opCodeWrapper{op} 328 329 jst.log["pc"] = pc 330 jst.log["op"] = ocw.toValue(jst.vm) 331 jst.log["gas"] = gas 332 jst.log["gasPrice"] = cost 333 jst.log["memory"] = jst.memvalue 334 jst.log["stack"] = jst.stackvalue 335 jst.log["contract"] = jst.contractvalue 336 jst.log["depth"] = depth 337 jst.log["account"] = contract.Address() 338 jst.log["err"] = err 339 340 _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) 341 if err != nil { 342 jst.err = wrapError("step", err) 343 } 344 } 345 return nil 346 } 347 348 // CaptureEnd is called after the call finishes 349 func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { 350 //TODO! @Arachnid please figure out of there's anything we can use this method for 351 return nil 352 } 353 354 // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error 355 func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { 356 if jst.err != nil { 357 return nil, jst.err 358 } 359 360 result, err = jst.callSafely("result") 361 if err != nil { 362 err = wrapError("result", err) 363 } 364 return 365 }