tracers: replace duktape with goja (#4113)

Reasons:
* duktape is not maintained
* on macOS it produces a warning: unused function '_duk_debugger_attach'
  (this slows down incremental builds and pollutes the test logs)

Why goja?
Geth has migrated some parts to goja (console and clef signer tool),
although not migrated tracers yet.

* fix isPrecompiled() native function
* recursion limit test is obsolete
This commit is contained in:
battlmonstr 2022-05-11 01:50:28 +02:00 committed by GitHub
parent 15ddd32e75
commit 65966f765a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 485 additions and 62 deletions

238
eth/tracers/jsvm.go Normal file
View File

@ -0,0 +1,238 @@
package tracers
import (
"github.com/dop251/goja"
"unsafe"
)
type JSVM struct {
vm *goja.Runtime
stack []goja.Value
}
func JSVMNew() *JSVM {
return &JSVM{
vm: goja.New(),
stack: make([]goja.Value, 0, 100),
}
}
func (vm *JSVM) Pop() {
vm.stack = vm.stack[:len(vm.stack)-1]
}
func (vm *JSVM) Swap(index1 int, index2 int) {
t := vm.stack[len(vm.stack)+index1]
vm.stack[len(vm.stack)+index1] = vm.stack[len(vm.stack)+index2]
vm.stack[len(vm.stack)+index2] = t
}
func (vm *JSVM) pushAny(val interface{}) {
vm.stack = append(vm.stack, vm.vm.ToValue(val))
}
func (vm *JSVM) PushBoolean(val bool) {
vm.pushAny(val)
}
func (vm *JSVM) PushInt(val int) {
vm.pushAny(val)
}
func (vm *JSVM) PushUint(val uint) {
vm.pushAny(val)
}
func (vm *JSVM) PushString(val string) string {
vm.pushAny(val)
return val
}
func (vm *JSVM) PushFixedBuffer(size int) unsafe.Pointer {
buf := make([]byte, size)
vm.pushAny(buf)
if size == 0 {
return unsafe.Pointer(nil)
}
return unsafe.Pointer(&buf[0])
}
func (vm *JSVM) PushGoFunction(fn0 func(*JSVM) int) {
fn := func(this goja.Value, args ...goja.Value) (goja.Value, error) {
vm.stack = append(vm.stack, this)
vm.stack = append(vm.stack, args...)
_ = fn0(vm)
result := vm.stack[len(vm.stack)-1]
vm.Pop()
return result, nil
}
vm.pushAny(fn)
}
func (vm *JSVM) PushObject() int {
vm.stack = append(vm.stack, vm.vm.ToValue(vm.vm.NewObject()))
return len(vm.stack) - 1
}
func (vm *JSVM) PushUndefined() {
vm.stack = append(vm.stack, goja.Undefined())
}
func (vm *JSVM) GetInt(index int) int {
return int(vm.stack[len(vm.stack)+index].Export().(int64))
}
func (vm *JSVM) GetString(index int) string {
return vm.stack[len(vm.stack)+index].Export().(string)
}
func (vm *JSVM) GetBuffer(index int) (rawPtr unsafe.Pointer, outSize uint) {
v := vm.stack[len(vm.stack)+index]
expValue := v.Export()
// toAddress() and some others are passed a string, but try to parse it with GetBuffer
if _, ok := expValue.(string); ok {
return nil, 0
}
buf := expValue.([]byte)
if len(buf) == 0 {
return unsafe.Pointer(nil), 0
}
return unsafe.Pointer(&buf[0]), uint(len(buf))
}
func (vm *JSVM) GetPropString(objIndex int, key string) bool {
obj := vm.stack[objIndex].ToObject(vm.vm)
v := obj.Get(key)
vm.stack = append(vm.stack, v)
return !goja.IsUndefined(v)
}
func (vm *JSVM) PutPropString(objIndex int, key string) {
v := vm.stack[len(vm.stack)-1]
vm.Pop()
obj := vm.stack[objIndex].ToObject(vm.vm)
err := obj.Set(key, v)
if err != nil {
panic(err)
}
}
func (vm *JSVM) GetGlobalString(key string) bool {
v := vm.vm.GlobalObject().Get(key)
vm.stack = append(vm.stack, v)
return !goja.IsUndefined(v)
}
func (vm *JSVM) PutGlobalString(key string) {
v := vm.stack[len(vm.stack)-1]
vm.Pop()
obj := vm.vm.GlobalObject()
err := obj.Set(key, v)
if err != nil {
panic(err)
}
}
func (vm *JSVM) PushGlobalGoFunction(name string, fn0 func(*JSVM) int) {
fn := func(this goja.Value, args ...goja.Value) (goja.Value, error) {
vm.stack = append(vm.stack, this)
vm.stack = append(vm.stack, args...)
_ = fn0(vm)
result := vm.stack[len(vm.stack)-1]
vm.Pop()
return result, nil
}
err := vm.vm.GlobalObject().Set(name, goja.Callable(fn))
if err != nil {
panic(err)
}
}
func (vm *JSVM) PushGlobalObject() int {
vm.stack = append(vm.stack, vm.vm.GlobalObject())
return len(vm.stack) - 1
}
func (vm *JSVM) Call(numArgs int) {
if vm.Pcall(numArgs) != 0 {
err := vm.stack[len(vm.stack)-1]
vm.Pop()
panic(err)
}
}
func (vm *JSVM) Pcall(numArgs int) int {
fnValue := vm.stack[len(vm.stack)-numArgs-1]
args := vm.stack[len(vm.stack)-numArgs:]
vm.stack = vm.stack[:len(vm.stack)-numArgs-1]
fn, ok := goja.AssertFunction(fnValue)
if !ok {
panic("AssertFunction")
}
v, err := fn(goja.Undefined(), args...)
if err != nil {
vm.stack = append(vm.stack, vm.vm.ToValue(err))
return 1
} else {
vm.stack = append(vm.stack, v)
return 0
}
}
func (vm *JSVM) PcallProp(objIndex int, numArgs int) int {
key := vm.stack[len(vm.stack)-numArgs-1].String()
args := vm.stack[len(vm.stack)-numArgs:]
vm.stack = vm.stack[:len(vm.stack)-numArgs-1]
obj := vm.stack[objIndex].ToObject(vm.vm)
fnValue := obj.Get(key)
fn, ok := goja.AssertFunction(fnValue)
if !ok {
panic("AssertFunction")
}
v, err := fn(obj, args...)
if err != nil {
vm.stack = append(vm.stack, vm.vm.ToValue(err))
return 1
} else {
vm.stack = append(vm.stack, v)
return 0
}
}
func (vm *JSVM) SafeToString(index int) string {
v := vm.stack[len(vm.stack)+index]
return v.ToString().String()
}
func (vm *JSVM) Eval() {
src := vm.GetString(-1)
vm.Pop()
vm.EvalString(src)
}
func (vm *JSVM) EvalString(src string) {
v, err := vm.vm.RunString(src)
if err != nil {
panic(err)
}
vm.stack = append(vm.stack, v)
}
func (vm *JSVM) PevalString(src string) error {
v, err := vm.vm.RunString(src)
if err != nil {
vm.stack = append(vm.stack, vm.vm.ToValue(err))
} else {
vm.stack = append(vm.stack, v)
}
return err
}

177
eth/tracers/jsvm_test.go Normal file
View File

@ -0,0 +1,177 @@
package tracers
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestSwap(t *testing.T) {
vm := JSVMNew()
vm.PushInt(1)
vm.PushInt(2)
vm.Swap(-1, -2)
assert.Equal(t, 1, vm.GetInt(-1))
vm.Pop()
assert.Equal(t, 2, vm.GetInt(-1))
}
func TestPushAndGetInt(t *testing.T) {
vm := JSVMNew()
vm.PushInt(123)
assert.Equal(t, 123, vm.GetInt(-1))
}
func TestPushAndGetString(t *testing.T) {
vm := JSVMNew()
vm.PushString("hello")
assert.Equal(t, "hello", vm.GetString(-1))
}
func TestPushAndGetBuffer(t *testing.T) {
vm := JSVMNew()
vm.PushFixedBuffer(1)
p, s := vm.GetBuffer(-1)
assert.Equal(t, uint(1), s)
assert.Equal(t, byte(0), *(*byte)(p))
}
func TestPutAndGetPropString(t *testing.T) {
vm := JSVMNew()
objIndex := vm.PushObject()
vm.PushString("hello")
vm.PutPropString(objIndex, "x")
exists := vm.GetPropString(objIndex, "x")
assert.Equal(t, true, exists)
x := vm.GetString(-1)
assert.Equal(t, "hello", x)
}
func TestGetGlobalString(t *testing.T) {
vm := JSVMNew()
vm.EvalString("x = 'hello'")
exists := vm.GetGlobalString("x")
assert.Equal(t, true, exists)
x := vm.GetString(-1)
assert.Equal(t, "hello", x)
}
func TestPutGlobalString(t *testing.T) {
vm := JSVMNew()
vm.PushString("hello")
vm.PutGlobalString("x")
exists := vm.GetGlobalString("x")
assert.Equal(t, true, exists)
x := vm.GetString(-1)
assert.Equal(t, "hello", x)
}
func TestCall0(t *testing.T) {
vm := JSVMNew()
vm.PushGoFunction(func(ctx *JSVM) int {
ctx.PushInt(123)
return 1
})
vm.Call(0)
x := vm.GetInt(-1)
assert.Equal(t, 123, x)
}
func TestCall1(t *testing.T) {
vm := JSVMNew()
vm.PushGoFunction(func(ctx *JSVM) int {
arg := ctx.GetInt(-1)
ctx.Pop()
ctx.PushInt(arg + 120)
return 1
})
vm.PushInt(3)
vm.Call(1)
x := vm.GetInt(-1)
assert.Equal(t, 123, x)
}
func TestCallPropWithGoFunction(t *testing.T) {
vm := JSVMNew()
vm.PushGlobalGoFunction("f", func(ctx *JSVM) int {
ctx.PushInt(123)
return 1
})
objIndex := vm.PushGlobalObject()
vm.PushString("f")
errCode := vm.PcallProp(objIndex, 0)
assert.Equal(t, 0, errCode)
x := vm.GetInt(-1)
assert.Equal(t, 123, x)
}
func TestCallProp0(t *testing.T) {
vm := JSVMNew()
vm.EvalString("function f() { return 'hello' }")
objIndex := vm.PushGlobalObject()
vm.PushString("f")
errCode := vm.PcallProp(objIndex, 0)
assert.Equal(t, 0, errCode)
x := vm.GetString(-1)
assert.Equal(t, "hello", x)
}
func TestCallProp1(t *testing.T) {
vm := JSVMNew()
vm.EvalString("function f(s) { return s + '123' }")
objIndex := vm.PushGlobalObject()
vm.PushString("f")
vm.PushString("hello")
errCode := vm.PcallProp(objIndex, 1)
assert.Equal(t, 0, errCode)
x := vm.GetString(-1)
assert.Equal(t, "hello123", x)
}
func TestCallPropWithObj(t *testing.T) {
vm := JSVMNew()
vm.EvalString("function f(opts) { return opts.name + '123' }")
globalIndex := vm.PushGlobalObject()
vm.PushString("f")
optsIndex := vm.PushObject()
vm.PushString("hello")
vm.PutPropString(optsIndex, "name")
errCode := vm.PcallProp(globalIndex, 1)
assert.Equal(t, 0, errCode)
x := vm.GetString(-1)
assert.Equal(t, "hello123", x)
}
func TestCallPropWithJSObj(t *testing.T) {
vm := JSVMNew()
vm.EvalString(`
function Options() { }
Options.prototype.name = function () { return 'hello' }
function makeOptions() { return new Options() }
function f(opts) { return opts.name() + '123' }
`)
globalIndex := vm.PushGlobalObject()
vm.PushString("f")
vm.PushString("makeOptions")
errCode := vm.PcallProp(globalIndex, 0)
assert.Equal(t, 0, errCode)
errCode = vm.PcallProp(globalIndex, 1)
assert.Equal(t, 0, errCode)
x := vm.GetString(-1)
assert.Equal(t, "hello123", x)
}
func TestSafeToString(t *testing.T) {
vm := JSVMNew()
vm.PushInt(5)
assert.Equal(t, "5", vm.SafeToString(-1))
}
func TestEval(t *testing.T) {
vm := JSVMNew()
vm.PushString("2 + 3")
vm.Eval()
x := vm.GetInt(-1)
assert.Equal(t, 5, x)
}

View File

@ -27,7 +27,6 @@ import (
"unsafe"
"github.com/holiman/uint256"
"gopkg.in/olebedev/go-duktape.v3"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
@ -57,14 +56,14 @@ func makeSlice(ptr unsafe.Pointer, size uint) []byte {
}
// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
func popSlice(ctx *duktape.Context) []byte {
func popSlice(ctx *JSVM) []byte {
blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
ctx.Pop()
return blob
}
// pushBigInt create a JavaScript BigInteger in the VM.
func pushBigInt(n *big.Int, ctx *duktape.Context) {
func pushBigInt(n *big.Int, ctx *JSVM) {
ctx.GetGlobalString("bigInt")
ctx.PushString(n.String())
ctx.Call(1)
@ -77,16 +76,16 @@ type opWrapper struct {
// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
// onto the VM stack.
func (ow *opWrapper) pushObject(vm *duktape.Context) {
func (ow *opWrapper) pushObject(vm *JSVM) {
obj := vm.PushObject()
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushInt(int(ow.op)); return 1 })
vm.PutPropString(obj, "toNumber")
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushString(ow.op.String()); return 1 })
vm.PutPropString(obj, "toString")
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
vm.PutPropString(obj, "isPush")
}
@ -128,13 +127,14 @@ func (mw *memoryWrapper) getUint(addr int64) *big.Int {
// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
// onto the VM stack.
func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
func (mw *memoryWrapper) pushObject(vm *JSVM) {
obj := vm.PushObject()
// Generate the `slice` method which takes two ints and returns a buffer
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
ctx.Pop2()
ctx.Pop()
ctx.Pop()
ptr := ctx.PushFixedBuffer(len(blob))
copy(makeSlice(ptr, uint(len(blob))), blob)
@ -143,7 +143,7 @@ func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "slice")
// Generate the `getUint` method which takes an int and returns a bigint
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
offset := int64(ctx.GetInt(-1))
ctx.Pop()
@ -171,14 +171,14 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
// onto the VM stack.
func (sw *stackWrapper) pushObject(vm *duktape.Context) {
func (sw *stackWrapper) pushObject(vm *JSVM) {
obj := vm.PushObject()
vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(sw.stack.Len()); return 1 })
vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushInt(sw.stack.Len()); return 1 })
vm.PutPropString(obj, "length")
// Generate the `peek` method which takes an int and returns a bigint
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
offset := ctx.GetInt(-1)
ctx.Pop()
@ -195,25 +195,25 @@ type dbWrapper struct {
// pushObject assembles a JSVM object wrapping a swappable database and pushes it
// onto the VM stack.
func (dw *dbWrapper) pushObject(vm *duktape.Context) {
func (dw *dbWrapper) pushObject(vm *JSVM) {
obj := vm.PushObject()
// Push the wrapper for statedb.GetBalance
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))).ToBig(), ctx)
return 1
})
vm.PutPropString(obj, "getBalance")
// Push the wrapper for statedb.GetNonce
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
return 1
})
vm.PutPropString(obj, "getNonce")
// Push the wrapper for statedb.GetCode
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
ptr := ctx.PushFixedBuffer(len(code))
@ -223,7 +223,7 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "getCode")
// Push the wrapper for statedb.GetState
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
hash := popSlice(ctx)
addr := popSlice(ctx)
@ -238,7 +238,7 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "getState")
// Push the wrapper for statedb.Exists
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
return 1
})
@ -252,11 +252,11 @@ type contractWrapper struct {
// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
// onto the VM stack.
func (cw *contractWrapper) pushObject(vm *duktape.Context) {
func (cw *contractWrapper) pushObject(vm *JSVM) {
obj := vm.PushObject()
// Push the wrapper for contract.Caller
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
ptr := ctx.PushFixedBuffer(20)
copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
return 1
@ -264,7 +264,7 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "getCaller")
// Push the wrapper for contract.Address
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
ptr := ctx.PushFixedBuffer(20)
copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
return 1
@ -272,14 +272,14 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "getAddress")
// Push the wrapper for contract.Value
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
pushBigInt(cw.contract.Value().ToBig(), ctx)
return 1
})
vm.PutPropString(obj, "getValue")
// Push the wrapper for contract.Input
vm.PushGoFunction(func(ctx *duktape.Context) int {
vm.PushGoFunction(func(ctx *JSVM) int {
blob := cw.contract.Input
ptr := ctx.PushFixedBuffer(len(blob))
@ -292,7 +292,7 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
// Tracer provides an implementation of Tracer that evaluates a Javascript
// function for each VM execution step.
type Tracer struct {
vm *duktape.Context // Javascript VM instance
vm *JSVM // Javascript VM instance
tracerObject int // Stack index of the tracer JavaScript object
stateObject int // Stack index of the global state to pull arguments from
@ -336,7 +336,7 @@ func New(code string, ctx *Context) (*Tracer, error) {
code = tracer
}
tracer := &Tracer{
vm: duktape.New(),
vm: JSVMNew(),
ctx: make(map[string]interface{}),
opWrapper: new(opWrapper),
stackWrapper: new(stackWrapper),
@ -359,11 +359,11 @@ func New(code string, ctx *Context) (*Tracer, error) {
}
// Set up builtins for this environment
tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("toHex", func(ctx *JSVM) int {
ctx.PushString(hexutil.Encode(popSlice(ctx)))
return 1
})
tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("toWord", func(ctx *JSVM) int {
var word common.Hash
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
word = common.BytesToHash(makeSlice(ptr, size))
@ -374,7 +374,7 @@ func New(code string, ctx *Context) (*Tracer, error) {
copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
return 1
})
tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *JSVM) int {
var addr common.Address
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
addr = common.BytesToAddress(makeSlice(ptr, size))
@ -385,7 +385,7 @@ func New(code string, ctx *Context) (*Tracer, error) {
copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
return 1
})
tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("toContract", func(ctx *JSVM) int {
var from common.Address
if ptr, size := ctx.GetBuffer(-2); ptr != nil {
from = common.BytesToAddress(makeSlice(ptr, size))
@ -393,13 +393,14 @@ func New(code string, ctx *Context) (*Tracer, error) {
from = common.HexToAddress(ctx.GetString(-2))
}
nonce := uint64(ctx.GetInt(-1))
ctx.Pop2()
ctx.Pop()
ctx.Pop()
contract := crypto.CreateAddress(from, nonce)
copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
return 1
})
tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *JSVM) int {
var from common.Address
if ptr, size := ctx.GetBuffer(-3); ptr != nil {
from = common.BytesToAddress(makeSlice(ptr, size))
@ -416,12 +417,14 @@ func New(code string, ctx *Context) (*Tracer, error) {
code = common.FromHex(ctx.GetString(-1))
}
codeHash := crypto.Keccak256(code)
ctx.Pop3()
ctx.Pop()
ctx.Pop()
ctx.Pop()
contract := crypto.CreateAddress2(from, salt, codeHash)
copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
return 1
})
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *JSVM) int {
addr := common.BytesToAddress(popSlice(ctx))
for _, p := range tracer.activePrecompiles {
if p == addr {
@ -429,15 +432,19 @@ func New(code string, ctx *Context) (*Tracer, error) {
return 1
}
}
ctx.PushBoolean(false)
_, ok := vm.PrecompiledContractsIstanbul[common.BytesToAddress(popSlice(ctx))]
ctx.PushBoolean(ok)
if _, ok := vm.PrecompiledContractsIstanbul[addr]; ok {
ctx.PushBoolean(true)
return 1
}
ctx.PushBoolean(false)
return 1
})
tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
tracer.vm.PushGlobalGoFunction("slice", func(ctx *JSVM) int {
start, end := ctx.GetInt(-2), ctx.GetInt(-1)
ctx.Pop2()
ctx.Pop()
ctx.Pop()
blob := popSlice(ctx)
size := end - start
@ -495,22 +502,22 @@ func New(code string, ctx *Context) (*Tracer, error) {
tracer.contractWrapper.pushObject(tracer.vm)
tracer.vm.PutPropString(logObject, "contract")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
tracer.vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushUint(*tracer.pcValue); return 1 })
tracer.vm.PutPropString(logObject, "getPC")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
tracer.vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushUint(*tracer.gasValue); return 1 })
tracer.vm.PutPropString(logObject, "getGas")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
tracer.vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushUint(*tracer.costValue); return 1 })
tracer.vm.PutPropString(logObject, "getCost")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
tracer.vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushUint(*tracer.depthValue); return 1 })
tracer.vm.PutPropString(logObject, "getDepth")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
tracer.vm.PushGoFunction(func(ctx *JSVM) int { ctx.PushUint(*tracer.refundValue); return 1 })
tracer.vm.PutPropString(logObject, "getRefund")
tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
tracer.vm.PushGoFunction(func(ctx *JSVM) int {
if tracer.errorValue != nil {
ctx.PushString(*tracer.errorValue)
} else {
@ -713,9 +720,6 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
if err != nil {
jst.err = wrapError("result", err)
}
// Clean up the JavaScript environment
jst.vm.DestroyHeap()
jst.vm.Destroy()
return result, jst.err
}

View File

@ -105,7 +105,7 @@ func TestTracer(t *testing.T) {
}{
{ // tests that we don't panic on bad arguments to memory access
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
want: `[{},{},{}]`,
want: `[[],[],[]]`,
}, { // tests that we don't panic on bad arguments to stack peeks
code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
want: `["0","0","0"]`,
@ -124,9 +124,6 @@ func TestTracer(t *testing.T) {
}, { // tests intrinsic gas
code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
want: `"100000.6.21000"`,
}, { // tests too deep object / serialization crash
code: "{step: function() {}, fault: function() {}, result: function() { var o={}; var x=o; for (var i=0; i<1000; i++){ o.foo={}; o=o.foo; } return x; }}",
fail: "RangeError: json encode recursion limit in server-side tracer function 'result'",
},
} {
if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err {

9
go.mod
View File

@ -16,6 +16,7 @@ require (
github.com/consensys/gnark-crypto v0.4.0
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0
github.com/emicklei/dot v0.16.0
github.com/emirpasic/gods v1.12.0
@ -63,8 +64,7 @@ require (
google.golang.org/grpc v1.46.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
google.golang.org/protobuf v1.28.0
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
modernc.org/sqlite v1.17.0
pgregory.net/rapid v0.4.7
)
@ -91,6 +91,7 @@ require (
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/etcd-io/bbolt v1.3.3 // indirect
github.com/flanglet/kanzi-go v1.9.1-0.20211212184056-72dda96261ee // indirect
@ -98,6 +99,7 @@ require (
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
@ -107,6 +109,7 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
@ -115,7 +118,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pion/datachannel v1.5.2 // indirect
github.com/pion/dtls/v2 v2.1.2 // indirect
github.com/pion/ice/v2 v2.1.20 // indirect
@ -141,6 +143,7 @@ require (
github.com/prometheus/procfs v0.7.2 // indirect
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/rs/dnscache v0.0.0-20210201191234-295bba877686 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect

16
go.sum
View File

@ -197,7 +197,12 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@ -255,6 +260,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
@ -438,6 +445,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -499,8 +507,6 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@ -1100,14 +1106,12 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=