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