Skip to content

Commit

Permalink
internal/jsre: upgrade offline wallet for goja
Browse files Browse the repository at this point in the history
  • Loading branch information
sadoci committed Oct 28, 2021
1 parent 66ded3c commit f26b65a
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 103 deletions.
18 changes: 9 additions & 9 deletions internal/jsre/jsre.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ func New(assetPath string, output io.Writer) *JSRE {
go re.runEventLoop()
re.Set("loadScript", MakeCallback(re.vm, re.loadScript))
re.Set("inspect", re.prettyPrintJS)
re.Set("loadFile", re.loadFile)
re.Set("msleep", re.msleep)
re.Set("offlineWalletOpen", re.offlineWalletOpen)
re.Set("offlineWalletAddress", re.offlineWalletAddress)
re.Set("offlineWalletClose", re.offlineWalletClose)
re.Set("offlineWalletSignTx", re.offlineWalletSignTx)
re.Set("offlineWalletList", re.offlineWalletList)
re.Set("loadFile", MakeCallback(re.vm, re.loadFile))
re.Set("msleep", MakeCallback(re.vm, re.msleep))
re.Set("offlineWalletOpen", MakeCallback(re.vm, re.offlineWalletOpen))
re.Set("offlineWalletAddress", MakeCallback(re.vm, re.offlineWalletAddress))
re.Set("offlineWalletClose", MakeCallback(re.vm, re.offlineWalletClose))
re.Set("offlineWalletSignTx", MakeCallback(re.vm, re.offlineWalletSignTx))
re.Set("offlineWalletList", MakeCallback(re.vm, re.offlineWalletList))
return re
}

Expand Down Expand Up @@ -330,8 +330,8 @@ func (re *JSRE) loadFile(call Call) (goja.Value, error) {
}

// msleep sleeps in ms resolution
func (re *JSRE) msleep(call Call) goja.Value {
func (re *JSRE) msleep(call Call) (goja.Value, error) {
delay := call.Argument(0).ToInteger()
time.Sleep(time.Duration(delay) * time.Millisecond)
return re.vm.ToValue(true)
return re.vm.ToValue(true), nil
}
130 changes: 36 additions & 94 deletions internal/jsre/offline_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strings"
"sync"

"github.com/dop251/goja"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
Expand All @@ -22,10 +23,8 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/pborman/uuid"
"github.com/robertkrimen/otto"
)

var (
Expand Down Expand Up @@ -66,17 +65,6 @@ type gethAccount struct {
key *keystore.Key
}

// copied from 'console' package.
// throwJSException panics on an otto.Value. The Otto VM will recover from the
// Go panic and throw msg as a JavaScript error.
func throwJSException(err error) otto.Value {
val, err2 := otto.ToValue(err.Error())
if err2 != nil {
log.Error("Failed to serialize JavaScript exception", "exception", err, "err", err2)
}
panic(val)
}

// id is sha3 of random uuid
func offlineWalletNewId() string {
return hex.EncodeToString(
Expand Down Expand Up @@ -165,28 +153,25 @@ func openUsbWallet(scheme, path string) (string, *common.Address, error) {
offlineWallets[id] = drv
addr, err := drv.Derive(accounts.DefaultBaseDerivationPath)
if err != nil {
throwJSException(err)
return "", nil, err
}
return id, &addr, nil
}

// { "id": string, "address": address } offlneWalletOpen(string url, string password)
func (re *JSRE) offlineWalletOpen(call otto.FunctionCall) otto.Value {
rawurl, err := call.Argument(0).ToString()
if err != nil {
throwJSException(err)
}
func (re *JSRE) offlineWalletOpen(call Call) (goja.Value, error) {
rawurl := call.Argument(0).ToString().String()
password := ""
if call.Argument(1).IsString() {
password, _ = call.Argument(1).ToString()
if len(call.Arguments) >= 2 && call.Argument(1).ToString() != nil {
password = call.Argument(1).ToString().String()
}

offlineWalletLock.Lock()
defer offlineWalletLock.Unlock()

u, err := url.Parse(rawurl)
if err != nil {
throwJSException(err)
return nil, err
}

path := u.Path
Expand All @@ -199,86 +184,62 @@ func (re *JSRE) offlineWalletOpen(call otto.FunctionCall) otto.Value {
case "", "geth", "gmet":
id, addr, err := loadGethAccount(password, path)
if err != nil {
throwJSException(err)
return nil, err
} else {
r := map[string]string{
"id": id,
"address": addr.Hex(),
}
env := otto.New()
v, err := env.ToValue(r)
if err != nil {
throwJSException(err)
}
return v
return re.vm.ToValue(r), nil
}
case "ledger", "trezor":
id, addr, err := openUsbWallet(u.Scheme, path)
if err != nil {
throwJSException(err)
return nil, err
} else {
r := map[string]string{
"id": id,
"address": addr.Hex(),
}
env := otto.New()
v, err := env.ToValue(r)
if err != nil {
throwJSException(err)
}
return v
return re.vm.ToValue(r), nil
}
default:
// not supported
throwJSException(errors.New("Not Supported"))
return nil, errors.New("Not Supported")
}
return otto.UndefinedValue()
}

// address offlneWalletAddress(string id)
func (re *JSRE) offlineWalletAddress(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToString()
if err != nil {
throwJSException(err)
}
func (re *JSRE) offlineWalletAddress(call Call) (goja.Value, error) {
id := call.Argument(0).ToString().String()

offlineWalletLock.Lock()
w, ok := offlineWallets[id]
offlineWalletLock.Unlock()

if !ok {
throwJSException(ethereum.NotFound)
return otto.UndefinedValue()
return nil, ethereum.NotFound
} else {
addr, err := w.Derive(accounts.DefaultBaseDerivationPath)
if err != nil {
throwJSException(err)
}
v, err := otto.ToValue(addr.Hex())
if err != nil {
throwJSException(err)
return nil, err
}
return v
return re.vm.ToValue(addr.Hex()), nil
}
}

// address offlneWalletClose(string id)
func (re *JSRE) offlineWalletClose(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToString()
if err != nil {
throwJSException(err)
}

func (re *JSRE) offlineWalletClose(call Call) (goja.Value, error) {
id := call.Argument(0).ToString().String()
offlineWalletLock.Lock()
defer offlineWalletLock.Unlock()

if w, ok := offlineWallets[id]; !ok {
throwJSException(ethereum.NotFound)
return otto.FalseValue()
return re.vm.ToValue(false), nil
} else {
delete(offlineWallets, id)
w.Close()
return otto.TrueValue()
return re.vm.ToValue(true), nil
}
}

Expand Down Expand Up @@ -382,31 +343,22 @@ func (re *JSRE) getTxArgs(jtx string) (*SendTxArgs, error) {
}

// []byte offlineWalletSignTx(string id, transaction tx, chainID int)
func (re *JSRE) offlineWalletSignTx(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToString()
if err != nil {
throwJSException(err)
}
chainID, err := call.Argument(2).ToInteger()
if err != nil {
throwJSException(err)
}
func (re *JSRE) offlineWalletSignTx(call Call) (goja.Value, error) {
id := call.Argument(0).ToString().String()
chainID := call.Argument(2).ToInteger()

var (
tx *types.Transaction
input []byte
)

// javascript json object -> string -> jsTx -> types.Transaction
JSON, _ := call.Otto.Object("JSON")
jtx, err := JSON.Call("stringify", call.Argument(1))
jtx, err := call.Argument(1).ToObject(re.vm).MarshalJSON()
if err != nil {
throwJSException(err)
return nil, err
}

txargs, err := re.getTxArgs(jtx.String())
txargs, err := re.getTxArgs(string(jtx))
if err != nil {
throwJSException(err)
return nil, err
}

if txargs.Data != nil {
Expand All @@ -429,41 +381,31 @@ func (re *JSRE) offlineWalletSignTx(call otto.FunctionCall) otto.Value {
offlineWalletLock.Unlock()

if !ok {
throwJSException(ethereum.NotFound)
return otto.UndefinedValue()
return nil, ethereum.NotFound
} else {
_, stx, err := w.SignTx(accounts.DefaultBaseDerivationPath, tx,
big.NewInt(chainID))
if err != nil {
throwJSException(err)
return nil, err
}
data, err := rlp.EncodeToBytes(stx)
if err != nil {
throwJSException(err)
return nil, err
}
v, err := otto.ToValue(hexutil.Encode(data))
if err != nil {
throwJSException(err)
}
return v
return re.vm.ToValue(hexutil.Encode(data)), nil
}
}

// offlineWalletList returns the array of ledger or trezor device paths
// for mostly informational use
func (re *JSRE) offlineWalletList(call otto.FunctionCall) otto.Value {
func (re *JSRE) offlineWalletList(call Call) (goja.Value, error) {
scheme := ""
if call.Argument(0).IsString() {
scheme, _ = call.Argument(0).ToString()
if len(call.Arguments) >= 1 && call.Argument(0).ToString() != nil {
scheme = call.Argument(0).ToString().String()
}

paths := usbwallet.ListDevices(scheme)
env := otto.New()
v, err := env.ToValue(paths)
if err != nil {
throwJSException(err)
}
return v
return re.vm.ToValue(paths), nil
}

// EOF

0 comments on commit f26b65a

Please sign in to comment.