tracer.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. // Copyright 2017 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. // package js is a collection of tracers written in javascript.
  17. package js
  18. import (
  19. "encoding/json"
  20. "errors"
  21. "fmt"
  22. "math/big"
  23. "strings"
  24. "sync/atomic"
  25. "time"
  26. "unicode"
  27. "unsafe"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/common/hexutil"
  30. "github.com/ethereum/go-ethereum/core"
  31. "github.com/ethereum/go-ethereum/core/vm"
  32. "github.com/ethereum/go-ethereum/crypto"
  33. tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
  34. "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
  35. "github.com/ethereum/go-ethereum/log"
  36. "gopkg.in/olebedev/go-duktape.v3"
  37. )
  38. // camel converts a snake cased input string into a camel cased output.
  39. func camel(str string) string {
  40. pieces := strings.Split(str, "_")
  41. for i := 1; i < len(pieces); i++ {
  42. pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
  43. }
  44. return strings.Join(pieces, "")
  45. }
  46. var assetTracers = make(map[string]string)
  47. // init retrieves the JavaScript transaction tracers included in go-ethereum.
  48. func init() {
  49. for _, file := range tracers.AssetNames() {
  50. name := camel(strings.TrimSuffix(file, ".js"))
  51. assetTracers[name] = string(tracers.MustAsset(file))
  52. }
  53. tracers2.RegisterLookup(true, newJsTracer)
  54. }
  55. // makeSlice convert an unsafe memory pointer with the given type into a Go byte
  56. // slice.
  57. //
  58. // Note, the returned slice uses the same memory area as the input arguments.
  59. // If those are duktape stack items, popping them off **will** make the slice
  60. // contents change.
  61. func makeSlice(ptr unsafe.Pointer, size uint) []byte {
  62. var sl = struct {
  63. addr uintptr
  64. len int
  65. cap int
  66. }{uintptr(ptr), int(size), int(size)}
  67. return *(*[]byte)(unsafe.Pointer(&sl))
  68. }
  69. // popSlice pops a buffer off the JavaScript stack and returns it as a slice.
  70. func popSlice(ctx *duktape.Context) []byte {
  71. blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
  72. ctx.Pop()
  73. return blob
  74. }
  75. // pushBigInt create a JavaScript BigInteger in the VM.
  76. func pushBigInt(n *big.Int, ctx *duktape.Context) {
  77. ctx.GetGlobalString("bigInt")
  78. ctx.PushString(n.String())
  79. ctx.Call(1)
  80. }
  81. // opWrapper provides a JavaScript wrapper around OpCode.
  82. type opWrapper struct {
  83. op vm.OpCode
  84. }
  85. // pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
  86. // onto the VM stack.
  87. func (ow *opWrapper) pushObject(vm *duktape.Context) {
  88. obj := vm.PushObject()
  89. vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
  90. vm.PutPropString(obj, "toNumber")
  91. vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
  92. vm.PutPropString(obj, "toString")
  93. vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
  94. vm.PutPropString(obj, "isPush")
  95. }
  96. // memoryWrapper provides a JavaScript wrapper around vm.Memory.
  97. type memoryWrapper struct {
  98. memory *vm.Memory
  99. }
  100. // slice returns the requested range of memory as a byte slice.
  101. func (mw *memoryWrapper) slice(begin, end int64) []byte {
  102. if end == begin {
  103. return []byte{}
  104. }
  105. if end < begin || begin < 0 {
  106. // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
  107. // runtime goes belly up https://github.com/golang/go/issues/15639.
  108. log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
  109. return nil
  110. }
  111. if mw.memory.Len() < int(end) {
  112. // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
  113. // runtime goes belly up https://github.com/golang/go/issues/15639.
  114. log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
  115. return nil
  116. }
  117. return mw.memory.GetCopy(begin, end-begin)
  118. }
  119. // getUint returns the 32 bytes at the specified address interpreted as a uint.
  120. func (mw *memoryWrapper) getUint(addr int64) *big.Int {
  121. if mw.memory.Len() < int(addr)+32 || addr < 0 {
  122. // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
  123. // runtime goes belly up https://github.com/golang/go/issues/15639.
  124. log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
  125. return new(big.Int)
  126. }
  127. return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
  128. }
  129. // pushObject assembles a JSVM object wrapping a swappable memory and pushes it
  130. // onto the VM stack.
  131. func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
  132. obj := vm.PushObject()
  133. // Generate the `slice` method which takes two ints and returns a buffer
  134. vm.PushGoFunction(func(ctx *duktape.Context) int {
  135. blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
  136. ctx.Pop2()
  137. ptr := ctx.PushFixedBuffer(len(blob))
  138. copy(makeSlice(ptr, uint(len(blob))), blob)
  139. return 1
  140. })
  141. vm.PutPropString(obj, "slice")
  142. // Generate the `getUint` method which takes an int and returns a bigint
  143. vm.PushGoFunction(func(ctx *duktape.Context) int {
  144. offset := int64(ctx.GetInt(-1))
  145. ctx.Pop()
  146. pushBigInt(mw.getUint(offset), ctx)
  147. return 1
  148. })
  149. vm.PutPropString(obj, "getUint")
  150. }
  151. // stackWrapper provides a JavaScript wrapper around vm.Stack.
  152. type stackWrapper struct {
  153. stack *vm.Stack
  154. }
  155. // peek returns the nth-from-the-top element of the stack.
  156. func (sw *stackWrapper) peek(idx int) *big.Int {
  157. if len(sw.stack.Data()) <= idx || idx < 0 {
  158. // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
  159. // runtime goes belly up https://github.com/golang/go/issues/15639.
  160. log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
  161. return new(big.Int)
  162. }
  163. return sw.stack.Back(idx).ToBig()
  164. }
  165. // pushObject assembles a JSVM object wrapping a swappable stack and pushes it
  166. // onto the VM stack.
  167. func (sw *stackWrapper) pushObject(vm *duktape.Context) {
  168. obj := vm.PushObject()
  169. vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
  170. vm.PutPropString(obj, "length")
  171. // Generate the `peek` method which takes an int and returns a bigint
  172. vm.PushGoFunction(func(ctx *duktape.Context) int {
  173. offset := ctx.GetInt(-1)
  174. ctx.Pop()
  175. pushBigInt(sw.peek(offset), ctx)
  176. return 1
  177. })
  178. vm.PutPropString(obj, "peek")
  179. }
  180. // dbWrapper provides a JavaScript wrapper around vm.Database.
  181. type dbWrapper struct {
  182. db vm.StateDB
  183. }
  184. // pushObject assembles a JSVM object wrapping a swappable database and pushes it
  185. // onto the VM stack.
  186. func (dw *dbWrapper) pushObject(vm *duktape.Context) {
  187. obj := vm.PushObject()
  188. // Push the wrapper for statedb.GetBalance
  189. vm.PushGoFunction(func(ctx *duktape.Context) int {
  190. pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
  191. return 1
  192. })
  193. vm.PutPropString(obj, "getBalance")
  194. // Push the wrapper for statedb.GetNonce
  195. vm.PushGoFunction(func(ctx *duktape.Context) int {
  196. ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
  197. return 1
  198. })
  199. vm.PutPropString(obj, "getNonce")
  200. // Push the wrapper for statedb.GetCode
  201. vm.PushGoFunction(func(ctx *duktape.Context) int {
  202. code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
  203. ptr := ctx.PushFixedBuffer(len(code))
  204. copy(makeSlice(ptr, uint(len(code))), code)
  205. return 1
  206. })
  207. vm.PutPropString(obj, "getCode")
  208. // Push the wrapper for statedb.GetState
  209. vm.PushGoFunction(func(ctx *duktape.Context) int {
  210. hash := popSlice(ctx)
  211. addr := popSlice(ctx)
  212. state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
  213. ptr := ctx.PushFixedBuffer(len(state))
  214. copy(makeSlice(ptr, uint(len(state))), state[:])
  215. return 1
  216. })
  217. vm.PutPropString(obj, "getState")
  218. // Push the wrapper for statedb.Exists
  219. vm.PushGoFunction(func(ctx *duktape.Context) int {
  220. ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
  221. return 1
  222. })
  223. vm.PutPropString(obj, "exists")
  224. }
  225. // contractWrapper provides a JavaScript wrapper around vm.Contract
  226. type contractWrapper struct {
  227. contract *vm.Contract
  228. }
  229. // pushObject assembles a JSVM object wrapping a swappable contract and pushes it
  230. // onto the VM stack.
  231. func (cw *contractWrapper) pushObject(vm *duktape.Context) {
  232. obj := vm.PushObject()
  233. // Push the wrapper for contract.Caller
  234. vm.PushGoFunction(func(ctx *duktape.Context) int {
  235. ptr := ctx.PushFixedBuffer(20)
  236. copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
  237. return 1
  238. })
  239. vm.PutPropString(obj, "getCaller")
  240. // Push the wrapper for contract.Address
  241. vm.PushGoFunction(func(ctx *duktape.Context) int {
  242. ptr := ctx.PushFixedBuffer(20)
  243. copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
  244. return 1
  245. })
  246. vm.PutPropString(obj, "getAddress")
  247. // Push the wrapper for contract.Value
  248. vm.PushGoFunction(func(ctx *duktape.Context) int {
  249. pushBigInt(cw.contract.Value(), ctx)
  250. return 1
  251. })
  252. vm.PutPropString(obj, "getValue")
  253. // Push the wrapper for contract.Input
  254. vm.PushGoFunction(func(ctx *duktape.Context) int {
  255. blob := cw.contract.Input
  256. ptr := ctx.PushFixedBuffer(len(blob))
  257. copy(makeSlice(ptr, uint(len(blob))), blob)
  258. return 1
  259. })
  260. vm.PutPropString(obj, "getInput")
  261. }
  262. type frame struct {
  263. typ *string
  264. from *common.Address
  265. to *common.Address
  266. input []byte
  267. gas *uint
  268. value *big.Int
  269. }
  270. func newFrame() *frame {
  271. return &frame{
  272. typ: new(string),
  273. from: new(common.Address),
  274. to: new(common.Address),
  275. gas: new(uint),
  276. }
  277. }
  278. func (f *frame) pushObject(vm *duktape.Context) {
  279. obj := vm.PushObject()
  280. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
  281. vm.PutPropString(obj, "getType")
  282. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
  283. vm.PutPropString(obj, "getFrom")
  284. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
  285. vm.PutPropString(obj, "getTo")
  286. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
  287. vm.PutPropString(obj, "getInput")
  288. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
  289. vm.PutPropString(obj, "getGas")
  290. vm.PushGoFunction(func(ctx *duktape.Context) int {
  291. if f.value != nil {
  292. pushValue(ctx, f.value)
  293. } else {
  294. ctx.PushUndefined()
  295. }
  296. return 1
  297. })
  298. vm.PutPropString(obj, "getValue")
  299. }
  300. type frameResult struct {
  301. gasUsed *uint
  302. output []byte
  303. errorValue *string
  304. }
  305. func newFrameResult() *frameResult {
  306. return &frameResult{
  307. gasUsed: new(uint),
  308. }
  309. }
  310. func (r *frameResult) pushObject(vm *duktape.Context) {
  311. obj := vm.PushObject()
  312. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
  313. vm.PutPropString(obj, "getGasUsed")
  314. vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
  315. vm.PutPropString(obj, "getOutput")
  316. vm.PushGoFunction(func(ctx *duktape.Context) int {
  317. if r.errorValue != nil {
  318. pushValue(ctx, *r.errorValue)
  319. } else {
  320. ctx.PushUndefined()
  321. }
  322. return 1
  323. })
  324. vm.PutPropString(obj, "getError")
  325. }
  326. // jsTracer provides an implementation of Tracer that evaluates a Javascript
  327. // function for each VM execution step.
  328. type jsTracer struct {
  329. vm *duktape.Context // Javascript VM instance
  330. env *vm.EVM // EVM instance executing the code being traced
  331. tracerObject int // Stack index of the tracer JavaScript object
  332. stateObject int // Stack index of the global state to pull arguments from
  333. opWrapper *opWrapper // Wrapper around the VM opcode
  334. stackWrapper *stackWrapper // Wrapper around the VM stack
  335. memoryWrapper *memoryWrapper // Wrapper around the VM memory
  336. contractWrapper *contractWrapper // Wrapper around the contract object
  337. dbWrapper *dbWrapper // Wrapper around the VM environment
  338. pcValue *uint // Swappable pc value wrapped by a log accessor
  339. gasValue *uint // Swappable gas value wrapped by a log accessor
  340. costValue *uint // Swappable cost value wrapped by a log accessor
  341. depthValue *uint // Swappable depth value wrapped by a log accessor
  342. errorValue *string // Swappable error value wrapped by a log accessor
  343. refundValue *uint // Swappable refund value wrapped by a log accessor
  344. frame *frame // Represents entry into call frame. Fields are swappable
  345. frameResult *frameResult // Represents exit from a call frame. Fields are swappable
  346. ctx map[string]interface{} // Transaction context gathered throughout execution
  347. err error // Error, if one has occurred
  348. interrupt uint32 // Atomic flag to signal execution interruption
  349. reason error // Textual reason for the interruption
  350. activePrecompiles []common.Address // Updated on CaptureStart based on given rules
  351. traceSteps bool // When true, will invoke step() on each opcode
  352. traceCallFrames bool // When true, will invoke enter() and exit() js funcs
  353. }
  354. // New instantiates a new tracer instance. code specifies a Javascript snippet,
  355. // which must evaluate to an expression returning an object with 'step', 'fault'
  356. // and 'result' functions.
  357. func newJsTracer(code string, ctx *tracers2.Context) (tracers2.Tracer, error) {
  358. if c, ok := assetTracers[code]; ok {
  359. code = c
  360. }
  361. if ctx == nil {
  362. ctx = new(tracers2.Context)
  363. }
  364. tracer := &jsTracer{
  365. vm: duktape.New(),
  366. ctx: make(map[string]interface{}),
  367. opWrapper: new(opWrapper),
  368. stackWrapper: new(stackWrapper),
  369. memoryWrapper: new(memoryWrapper),
  370. contractWrapper: new(contractWrapper),
  371. dbWrapper: new(dbWrapper),
  372. pcValue: new(uint),
  373. gasValue: new(uint),
  374. costValue: new(uint),
  375. depthValue: new(uint),
  376. refundValue: new(uint),
  377. frame: newFrame(),
  378. frameResult: newFrameResult(),
  379. }
  380. if ctx.BlockHash != (common.Hash{}) {
  381. tracer.ctx["blockHash"] = ctx.BlockHash
  382. if ctx.TxHash != (common.Hash{}) {
  383. tracer.ctx["txIndex"] = ctx.TxIndex
  384. tracer.ctx["txHash"] = ctx.TxHash
  385. }
  386. }
  387. // Set up builtins for this environment
  388. tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
  389. ctx.PushString(hexutil.Encode(popSlice(ctx)))
  390. return 1
  391. })
  392. tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
  393. var word common.Hash
  394. if ptr, size := ctx.GetBuffer(-1); ptr != nil {
  395. word = common.BytesToHash(makeSlice(ptr, size))
  396. } else {
  397. word = common.HexToHash(ctx.GetString(-1))
  398. }
  399. ctx.Pop()
  400. copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
  401. return 1
  402. })
  403. tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
  404. var addr common.Address
  405. if ptr, size := ctx.GetBuffer(-1); ptr != nil {
  406. addr = common.BytesToAddress(makeSlice(ptr, size))
  407. } else {
  408. addr = common.HexToAddress(ctx.GetString(-1))
  409. }
  410. ctx.Pop()
  411. copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
  412. return 1
  413. })
  414. tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
  415. var from common.Address
  416. if ptr, size := ctx.GetBuffer(-2); ptr != nil {
  417. from = common.BytesToAddress(makeSlice(ptr, size))
  418. } else {
  419. from = common.HexToAddress(ctx.GetString(-2))
  420. }
  421. nonce := uint64(ctx.GetInt(-1))
  422. ctx.Pop2()
  423. contract := crypto.CreateAddress(from, nonce)
  424. copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
  425. return 1
  426. })
  427. tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
  428. var from common.Address
  429. if ptr, size := ctx.GetBuffer(-3); ptr != nil {
  430. from = common.BytesToAddress(makeSlice(ptr, size))
  431. } else {
  432. from = common.HexToAddress(ctx.GetString(-3))
  433. }
  434. // Retrieve salt hex string from js stack
  435. salt := common.HexToHash(ctx.GetString(-2))
  436. // Retrieve code slice from js stack
  437. var code []byte
  438. if ptr, size := ctx.GetBuffer(-1); ptr != nil {
  439. code = common.CopyBytes(makeSlice(ptr, size))
  440. } else {
  441. code = common.FromHex(ctx.GetString(-1))
  442. }
  443. codeHash := crypto.Keccak256(code)
  444. ctx.Pop3()
  445. contract := crypto.CreateAddress2(from, salt, codeHash)
  446. copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
  447. return 1
  448. })
  449. tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
  450. addr := common.BytesToAddress(popSlice(ctx))
  451. for _, p := range tracer.activePrecompiles {
  452. if p == addr {
  453. ctx.PushBoolean(true)
  454. return 1
  455. }
  456. }
  457. ctx.PushBoolean(false)
  458. return 1
  459. })
  460. tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
  461. start, end := ctx.GetInt(-2), ctx.GetInt(-1)
  462. ctx.Pop2()
  463. blob := popSlice(ctx)
  464. size := end - start
  465. if start < 0 || start > end || end > len(blob) {
  466. // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
  467. // runtime goes belly up https://github.com/golang/go/issues/15639.
  468. log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
  469. ctx.PushFixedBuffer(0)
  470. return 1
  471. }
  472. copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
  473. return 1
  474. })
  475. // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
  476. if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
  477. log.Warn("Failed to compile tracer", "err", err)
  478. return nil, err
  479. }
  480. tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
  481. hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
  482. tracer.vm.Pop()
  483. if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
  484. return nil, fmt.Errorf("trace object must expose a function fault()")
  485. }
  486. tracer.vm.Pop()
  487. if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
  488. return nil, fmt.Errorf("trace object must expose a function result()")
  489. }
  490. tracer.vm.Pop()
  491. hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
  492. tracer.vm.Pop()
  493. hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
  494. tracer.vm.Pop()
  495. if hasEnter != hasExit {
  496. return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
  497. }
  498. tracer.traceCallFrames = hasEnter && hasExit
  499. tracer.traceSteps = hasStep
  500. // Tracer is valid, inject the big int library to access large numbers
  501. tracer.vm.EvalString(bigIntegerJS)
  502. tracer.vm.PutGlobalString("bigInt")
  503. // Push the global environment state as object #1 into the JSVM stack
  504. tracer.stateObject = tracer.vm.PushObject()
  505. logObject := tracer.vm.PushObject()
  506. tracer.opWrapper.pushObject(tracer.vm)
  507. tracer.vm.PutPropString(logObject, "op")
  508. tracer.stackWrapper.pushObject(tracer.vm)
  509. tracer.vm.PutPropString(logObject, "stack")
  510. tracer.memoryWrapper.pushObject(tracer.vm)
  511. tracer.vm.PutPropString(logObject, "memory")
  512. tracer.contractWrapper.pushObject(tracer.vm)
  513. tracer.vm.PutPropString(logObject, "contract")
  514. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
  515. tracer.vm.PutPropString(logObject, "getPC")
  516. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
  517. tracer.vm.PutPropString(logObject, "getGas")
  518. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
  519. tracer.vm.PutPropString(logObject, "getCost")
  520. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
  521. tracer.vm.PutPropString(logObject, "getDepth")
  522. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
  523. tracer.vm.PutPropString(logObject, "getRefund")
  524. tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
  525. if tracer.errorValue != nil {
  526. ctx.PushString(*tracer.errorValue)
  527. } else {
  528. ctx.PushUndefined()
  529. }
  530. return 1
  531. })
  532. tracer.vm.PutPropString(logObject, "getError")
  533. tracer.vm.PutPropString(tracer.stateObject, "log")
  534. tracer.frame.pushObject(tracer.vm)
  535. tracer.vm.PutPropString(tracer.stateObject, "frame")
  536. tracer.frameResult.pushObject(tracer.vm)
  537. tracer.vm.PutPropString(tracer.stateObject, "frameResult")
  538. tracer.dbWrapper.pushObject(tracer.vm)
  539. tracer.vm.PutPropString(tracer.stateObject, "db")
  540. return tracer, nil
  541. }
  542. // Stop terminates execution of the tracer at the first opportune moment.
  543. func (jst *jsTracer) Stop(err error) {
  544. jst.reason = err
  545. atomic.StoreUint32(&jst.interrupt, 1)
  546. }
  547. // call executes a method on a JS object, catching any errors, formatting and
  548. // returning them as error objects.
  549. func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
  550. // Execute the JavaScript call and return any error
  551. jst.vm.PushString(method)
  552. for _, arg := range args {
  553. jst.vm.GetPropString(jst.stateObject, arg)
  554. }
  555. code := jst.vm.PcallProp(jst.tracerObject, len(args))
  556. defer jst.vm.Pop()
  557. if code != 0 {
  558. err := jst.vm.SafeToString(-1)
  559. return nil, errors.New(err)
  560. }
  561. // No error occurred, extract return value and return
  562. if noret {
  563. return nil, nil
  564. }
  565. // Push a JSON marshaller onto the stack. We can't marshal from the out-
  566. // side because duktape can crash on large nestings and we can't catch
  567. // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?!
  568. jst.vm.PushString("(JSON.stringify)")
  569. jst.vm.Eval()
  570. jst.vm.Swap(-1, -2)
  571. if code = jst.vm.Pcall(1); code != 0 {
  572. err := jst.vm.SafeToString(-1)
  573. return nil, errors.New(err)
  574. }
  575. return json.RawMessage(jst.vm.SafeToString(-1)), nil
  576. }
  577. func wrapError(context string, err error) error {
  578. return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
  579. }
  580. // CaptureStart implements the Tracer interface to initialize the tracing operation.
  581. func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
  582. jst.env = env
  583. jst.ctx["type"] = "CALL"
  584. if create {
  585. jst.ctx["type"] = "CREATE"
  586. }
  587. jst.ctx["from"] = from
  588. jst.ctx["to"] = to
  589. jst.ctx["input"] = input
  590. jst.ctx["gas"] = gas
  591. jst.ctx["gasPrice"] = env.TxContext.GasPrice
  592. jst.ctx["value"] = value
  593. // Initialize the context
  594. jst.ctx["block"] = env.Context.BlockNumber.Uint64()
  595. jst.dbWrapper.db = env.StateDB
  596. // Update list of precompiles based on current block
  597. rules := env.ChainConfig().Rules(env.Context.BlockNumber)
  598. jst.activePrecompiles = vm.ActivePrecompiles(rules)
  599. // Compute intrinsic gas
  600. isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
  601. isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
  602. intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
  603. if err != nil {
  604. return
  605. }
  606. jst.ctx["intrinsicGas"] = intrinsicGas
  607. }
  608. // CaptureState implements the Tracer interface to trace a single step of VM execution.
  609. func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
  610. if !jst.traceSteps {
  611. return
  612. }
  613. if jst.err != nil {
  614. return
  615. }
  616. // If tracing was interrupted, set the error and stop
  617. if atomic.LoadUint32(&jst.interrupt) > 0 {
  618. jst.err = jst.reason
  619. jst.env.Cancel()
  620. return
  621. }
  622. jst.opWrapper.op = op
  623. jst.stackWrapper.stack = scope.Stack
  624. jst.memoryWrapper.memory = scope.Memory
  625. jst.contractWrapper.contract = scope.Contract
  626. *jst.pcValue = uint(pc)
  627. *jst.gasValue = uint(gas)
  628. *jst.costValue = uint(cost)
  629. *jst.depthValue = uint(depth)
  630. *jst.refundValue = uint(jst.env.StateDB.GetRefund())
  631. jst.errorValue = nil
  632. if err != nil {
  633. jst.errorValue = new(string)
  634. *jst.errorValue = err.Error()
  635. }
  636. if _, err := jst.call(true, "step", "log", "db"); err != nil {
  637. jst.err = wrapError("step", err)
  638. }
  639. }
  640. // CaptureFault implements the Tracer interface to trace an execution fault
  641. func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
  642. if jst.err != nil {
  643. return
  644. }
  645. // Apart from the error, everything matches the previous invocation
  646. jst.errorValue = new(string)
  647. *jst.errorValue = err.Error()
  648. if _, err := jst.call(true, "fault", "log", "db"); err != nil {
  649. jst.err = wrapError("fault", err)
  650. }
  651. }
  652. // CaptureEnd is called after the call finishes to finalize the tracing.
  653. func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
  654. jst.ctx["output"] = output
  655. jst.ctx["time"] = t.String()
  656. jst.ctx["gasUsed"] = gasUsed
  657. if err != nil {
  658. jst.ctx["error"] = err.Error()
  659. }
  660. }
  661. // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
  662. func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
  663. if !jst.traceCallFrames {
  664. return
  665. }
  666. if jst.err != nil {
  667. return
  668. }
  669. // If tracing was interrupted, set the error and stop
  670. if atomic.LoadUint32(&jst.interrupt) > 0 {
  671. jst.err = jst.reason
  672. return
  673. }
  674. *jst.frame.typ = typ.String()
  675. *jst.frame.from = from
  676. *jst.frame.to = to
  677. jst.frame.input = common.CopyBytes(input)
  678. *jst.frame.gas = uint(gas)
  679. jst.frame.value = nil
  680. if value != nil {
  681. jst.frame.value = new(big.Int).SetBytes(value.Bytes())
  682. }
  683. if _, err := jst.call(true, "enter", "frame"); err != nil {
  684. jst.err = wrapError("enter", err)
  685. }
  686. }
  687. // CaptureExit is called when EVM exits a scope, even if the scope didn't
  688. // execute any code.
  689. func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
  690. if !jst.traceCallFrames {
  691. return
  692. }
  693. // If tracing was interrupted, set the error and stop
  694. if atomic.LoadUint32(&jst.interrupt) > 0 {
  695. jst.err = jst.reason
  696. return
  697. }
  698. jst.frameResult.output = common.CopyBytes(output)
  699. *jst.frameResult.gasUsed = uint(gasUsed)
  700. jst.frameResult.errorValue = nil
  701. if err != nil {
  702. jst.frameResult.errorValue = new(string)
  703. *jst.frameResult.errorValue = err.Error()
  704. }
  705. if _, err := jst.call(true, "exit", "frameResult"); err != nil {
  706. jst.err = wrapError("exit", err)
  707. }
  708. }
  709. // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
  710. func (jst *jsTracer) GetResult() (json.RawMessage, error) {
  711. // Transform the context into a JavaScript object and inject into the state
  712. obj := jst.vm.PushObject()
  713. for key, val := range jst.ctx {
  714. jst.addToObj(obj, key, val)
  715. }
  716. jst.vm.PutPropString(jst.stateObject, "ctx")
  717. // Finalize the trace and return the results
  718. result, err := jst.call(false, "result", "ctx", "db")
  719. if err != nil {
  720. jst.err = wrapError("result", err)
  721. }
  722. // Clean up the JavaScript environment
  723. jst.vm.DestroyHeap()
  724. jst.vm.Destroy()
  725. return result, jst.err
  726. }
  727. // addToObj pushes a field to a JS object.
  728. func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
  729. pushValue(jst.vm, val)
  730. jst.vm.PutPropString(obj, key)
  731. }
  732. func pushValue(ctx *duktape.Context, val interface{}) {
  733. switch val := val.(type) {
  734. case uint64:
  735. ctx.PushUint(uint(val))
  736. case string:
  737. ctx.PushString(val)
  738. case []byte:
  739. ptr := ctx.PushFixedBuffer(len(val))
  740. copy(makeSlice(ptr, uint(len(val))), val)
  741. case common.Address:
  742. ptr := ctx.PushFixedBuffer(20)
  743. copy(makeSlice(ptr, 20), val[:])
  744. case *big.Int:
  745. pushBigInt(val, ctx)
  746. case int:
  747. ctx.PushInt(val)
  748. case uint:
  749. ctx.PushUint(val)
  750. case common.Hash:
  751. ptr := ctx.PushFixedBuffer(32)
  752. copy(makeSlice(ptr, 32), val[:])
  753. default:
  754. panic(fmt.Sprintf("unsupported type: %T", val))
  755. }
  756. }