From 594e038e75a1acf3d922d48afdcc2b7bdae40368 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <gballet@gmail.com>
Date: Wed, 29 Jan 2020 13:47:56 +0100
Subject: [PATCH] signer/rules: use goja and remove otto (#20599)

* signer: replace otto with goja

* go.mod: remove Otto
---
 go.mod                     |  1 -
 go.sum                     |  2 --
 signer/rules/rules.go      | 52 ++++++++++++++++++--------------------
 signer/rules/rules_test.go |  2 +-
 4 files changed, 26 insertions(+), 31 deletions(-)

diff --git a/go.mod b/go.mod
index c3b5b6fd0..b496b871b 100644
--- a/go.mod
+++ b/go.mod
@@ -49,7 +49,6 @@ require (
 	github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
 	github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150
 	github.com/rjeczalik/notify v0.9.1
-	github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d
 	github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00
 	github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 // indirect
 	github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
diff --git a/go.sum b/go.sum
index 81c718b02..f3ddb7ba0 100644
--- a/go.sum
+++ b/go.sum
@@ -167,8 +167,6 @@ github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150 h1:ZeU+auZj1iNzN
 github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
 github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
-github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d h1:ouzpe+YhpIfnjR40gSkJHWsvXmB6TiPKqMtMpfyU9DE=
-github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
 github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 h1:8DPul/X0IT/1TNMIxoKLwdemEOBBHDC/K4EB16Cw5WE=
 github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20=
diff --git a/signer/rules/rules.go b/signer/rules/rules.go
index cb375a62a..03e513673 100644
--- a/signer/rules/rules.go
+++ b/signer/rules/rules.go
@@ -22,12 +22,12 @@ import (
 	"os"
 	"strings"
 
+	"github.com/dop251/goja"
 	"github.com/ethereum/go-ethereum/internal/ethapi"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/signer/core"
 	"github.com/ethereum/go-ethereum/signer/rules/deps"
 	"github.com/ethereum/go-ethereum/signer/storage"
-	"github.com/robertkrimen/otto"
 )
 
 var (
@@ -36,13 +36,13 @@ var (
 
 // consoleOutput is an override for the console.log and console.error methods to
 // stream the output into the configured output stream instead of stdout.
-func consoleOutput(call otto.FunctionCall) otto.Value {
+func consoleOutput(call goja.FunctionCall) goja.Value {
 	output := []string{"JS:> "}
-	for _, argument := range call.ArgumentList {
+	for _, argument := range call.Arguments {
 		output = append(output, fmt.Sprintf("%v", argument))
 	}
 	fmt.Fprintln(os.Stderr, strings.Join(output, " "))
-	return otto.Value{}
+	return goja.Undefined()
 }
 
 // rulesetUI provides an implementation of UIClientAPI that evaluates a javascript
@@ -70,45 +70,47 @@ func (r *rulesetUI) Init(javascriptRules string) error {
 	r.jsRules = javascriptRules
 	return nil
 }
-func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (otto.Value, error) {
+func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (goja.Value, error) {
 
 	// Instantiate a fresh vm engine every time
-	vm := otto.New()
+	vm := goja.New()
 
 	// Set the native callbacks
-	consoleObj, _ := vm.Get("console")
-	consoleObj.Object().Set("log", consoleOutput)
-	consoleObj.Object().Set("error", consoleOutput)
+	consoleObj := vm.NewObject()
+	consoleObj.Set("log", consoleOutput)
+	consoleObj.Set("error", consoleOutput)
+	vm.Set("console", consoleObj)
 
-	vm.Set("storage", struct{}{})
-	storageObj, _ := vm.Get("storage")
-	storageObj.Object().Set("put", func(call otto.FunctionCall) otto.Value {
+	storageObj := vm.NewObject()
+	storageObj.Set("put", func(call goja.FunctionCall) goja.Value {
 		key, val := call.Argument(0).String(), call.Argument(1).String()
 		if val == "" {
 			r.storage.Del(key)
 		} else {
 			r.storage.Put(key, val)
 		}
-		return otto.NullValue()
+		return goja.Null()
 	})
-	storageObj.Object().Set("get", func(call otto.FunctionCall) otto.Value {
+	storageObj.Set("get", func(call goja.FunctionCall) goja.Value {
 		goval, _ := r.storage.Get(call.Argument(0).String())
-		jsval, _ := otto.ToValue(goval)
+		jsval := vm.ToValue(goval)
 		return jsval
 	})
+	vm.Set("storage", storageObj)
+
 	// Load bootstrap libraries
-	script, err := vm.Compile("bignumber.js", BigNumber_JS)
+	script, err := goja.Compile("bignumber.js", string(BigNumber_JS), true)
 	if err != nil {
 		log.Warn("Failed loading libraries", "err", err)
-		return otto.UndefinedValue(), err
+		return goja.Undefined(), err
 	}
-	vm.Run(script)
+	vm.RunProgram(script)
 
 	// Run the actual rule implementation
-	_, err = vm.Run(r.jsRules)
+	_, err = vm.RunString(r.jsRules)
 	if err != nil {
 		log.Warn("Execution failed", "err", err)
-		return otto.UndefinedValue(), err
+		return goja.Undefined(), err
 	}
 
 	// And the actual call
@@ -119,7 +121,7 @@ func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (otto.Value, error
 	jsonbytes, err := json.Marshal(jsarg)
 	if err != nil {
 		log.Warn("failed marshalling data", "data", jsarg)
-		return otto.UndefinedValue(), err
+		return goja.Undefined(), err
 	}
 	// Now, we call foobar(JSON.parse(<jsondata>)).
 	var call string
@@ -128,7 +130,7 @@ func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (otto.Value, error
 	} else {
 		call = fmt.Sprintf("%v()", jsfunc)
 	}
-	return vm.Run(call)
+	return vm.RunString(call)
 }
 
 func (r *rulesetUI) checkApproval(jsfunc string, jsarg []byte, err error) (bool, error) {
@@ -140,11 +142,7 @@ func (r *rulesetUI) checkApproval(jsfunc string, jsarg []byte, err error) (bool,
 		log.Info("error occurred during execution", "error", err)
 		return false, err
 	}
-	result, err := v.ToString()
-	if err != nil {
-		log.Info("error occurred during response unmarshalling", "error", err)
-		return false, err
-	}
+	result := v.ToString().String()
 	if result == "Approve" {
 		log.Info("Op approved")
 		return true, nil
diff --git a/signer/rules/rules_test.go b/signer/rules/rules_test.go
index c030ed47c..510c57e67 100644
--- a/signer/rules/rules_test.go
+++ b/signer/rules/rules_test.go
@@ -337,7 +337,7 @@ func TestStorage(t *testing.T) {
 	if err != nil {
 		t.Errorf("Unexpected error %v", err)
 	}
-	retval, err := v.ToString()
+	retval := v.ToString().String()
 
 	if err != nil {
 		t.Errorf("Unexpected error %v", err)
-- 
GitLab