github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/console/bridge.go (about)

     1  package console
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/neatlab/neatio/chain/accounts/usbwallet"
    11  	"github.com/neatlab/neatio/chain/log"
    12  	"github.com/neatlab/neatio/network/rpc"
    13  	"github.com/robertkrimen/otto"
    14  )
    15  
    16  type bridge struct {
    17  	client   *rpc.Client
    18  	prompter UserPrompter
    19  	printer  io.Writer
    20  }
    21  
    22  func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge {
    23  	return &bridge{
    24  		client:   client,
    25  		prompter: prompter,
    26  		printer:  printer,
    27  	}
    28  }
    29  
    30  func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
    31  	var (
    32  		password string
    33  		confirm  string
    34  		err      error
    35  	)
    36  	switch {
    37  
    38  	case len(call.ArgumentList) == 0:
    39  		if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
    40  			throwJSException(err.Error())
    41  		}
    42  		if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
    43  			throwJSException(err.Error())
    44  		}
    45  		if password != confirm {
    46  			throwJSException("passphrases don't match!")
    47  		}
    48  
    49  	case len(call.ArgumentList) == 1 && call.Argument(0).IsString():
    50  		password, _ = call.Argument(0).ToString()
    51  
    52  	default:
    53  		throwJSException("expected 0 or 1 string argument")
    54  	}
    55  
    56  	ret, err := call.Otto.Call("jeth.newAccount", nil, password)
    57  	if err != nil {
    58  		throwJSException(err.Error())
    59  	}
    60  	return ret
    61  }
    62  
    63  func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
    64  
    65  	if !call.Argument(0).IsString() {
    66  		throwJSException("first argument must be the wallet URL to open")
    67  	}
    68  	wallet := call.Argument(0)
    69  
    70  	var passwd otto.Value
    71  	if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
    72  		passwd, _ = otto.ToValue("")
    73  	} else {
    74  		passwd = call.Argument(1)
    75  	}
    76  
    77  	val, err := call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
    78  	if err == nil {
    79  		return val
    80  	}
    81  
    82  	if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) {
    83  		throwJSException(err.Error())
    84  	}
    85  
    86  	fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
    87  	fmt.Fprintf(b.printer, "7 | 8 | 9\n")
    88  	fmt.Fprintf(b.printer, "--+---+--\n")
    89  	fmt.Fprintf(b.printer, "4 | 5 | 6\n")
    90  	fmt.Fprintf(b.printer, "--+---+--\n")
    91  	fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
    92  
    93  	if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
    94  		throwJSException(err.Error())
    95  	} else {
    96  		passwd, _ = otto.ToValue(input)
    97  	}
    98  	if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
    99  		throwJSException(err.Error())
   100  	}
   101  	return val
   102  }
   103  
   104  func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
   105  
   106  	if !call.Argument(0).IsString() {
   107  		throwJSException("first argument must be the account to unlock")
   108  	}
   109  	account := call.Argument(0)
   110  
   111  	var passwd otto.Value
   112  
   113  	if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
   114  		fmt.Fprintf(b.printer, "Unlock account %s\n", account)
   115  		if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
   116  			throwJSException(err.Error())
   117  		} else {
   118  			passwd, _ = otto.ToValue(input)
   119  		}
   120  	} else {
   121  		if !call.Argument(1).IsString() {
   122  			throwJSException("password must be a string")
   123  		}
   124  		passwd = call.Argument(1)
   125  	}
   126  
   127  	duration := otto.NullValue()
   128  	if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() {
   129  		if !call.Argument(2).IsNumber() {
   130  			throwJSException("unlock duration must be a number")
   131  		}
   132  		duration = call.Argument(2)
   133  	}
   134  
   135  	val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration)
   136  	if err != nil {
   137  		throwJSException(err.Error())
   138  	}
   139  	return val
   140  }
   141  
   142  func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) {
   143  	var (
   144  		message = call.Argument(0)
   145  		account = call.Argument(1)
   146  		passwd  = call.Argument(2)
   147  	)
   148  
   149  	if !message.IsString() {
   150  		throwJSException("first argument must be the message to sign")
   151  	}
   152  	if !account.IsString() {
   153  		throwJSException("second argument must be the account to sign with")
   154  	}
   155  
   156  	if passwd.IsUndefined() || passwd.IsNull() {
   157  		fmt.Fprintf(b.printer, "Give password for account %s\n", account)
   158  		if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
   159  			throwJSException(err.Error())
   160  		} else {
   161  			passwd, _ = otto.ToValue(input)
   162  		}
   163  	}
   164  	if !passwd.IsString() {
   165  		throwJSException("third argument must be the password to unlock the account")
   166  	}
   167  
   168  	val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd)
   169  	if err != nil {
   170  		throwJSException(err.Error())
   171  	}
   172  	return val
   173  }
   174  
   175  func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) {
   176  	if call.Argument(0).IsNumber() {
   177  		sleep, _ := call.Argument(0).ToInteger()
   178  		time.Sleep(time.Duration(sleep) * time.Second)
   179  		return otto.TrueValue()
   180  	}
   181  	return throwJSException("usage: sleep(<number of seconds>)")
   182  }
   183  
   184  func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
   185  	var (
   186  		blocks = int64(0)
   187  		sleep  = int64(9999999999999999)
   188  	)
   189  
   190  	nArgs := len(call.ArgumentList)
   191  	if nArgs == 0 {
   192  		throwJSException("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
   193  	}
   194  	if nArgs >= 1 {
   195  		if call.Argument(0).IsNumber() {
   196  			blocks, _ = call.Argument(0).ToInteger()
   197  		} else {
   198  			throwJSException("expected number as first argument")
   199  		}
   200  	}
   201  	if nArgs >= 2 {
   202  		if call.Argument(1).IsNumber() {
   203  			sleep, _ = call.Argument(1).ToInteger()
   204  		} else {
   205  			throwJSException("expected number as second argument")
   206  		}
   207  	}
   208  
   209  	blockNumber := func() int64 {
   210  		result, err := call.Otto.Run("eth.blockNumber")
   211  		if err != nil {
   212  			throwJSException(err.Error())
   213  		}
   214  		block, err := result.ToInteger()
   215  		if err != nil {
   216  			throwJSException(err.Error())
   217  		}
   218  		return block
   219  	}
   220  
   221  	targetBlockNr := blockNumber() + blocks
   222  	deadline := time.Now().Add(time.Duration(sleep) * time.Second)
   223  
   224  	for time.Now().Before(deadline) {
   225  		if blockNumber() >= targetBlockNr {
   226  			return otto.TrueValue()
   227  		}
   228  		time.Sleep(time.Second)
   229  	}
   230  	return otto.FalseValue()
   231  }
   232  
   233  type jsonrpcCall struct {
   234  	Id     int64
   235  	Method string
   236  	Params []interface{}
   237  }
   238  
   239  func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
   240  
   241  	JSON, _ := call.Otto.Object("JSON")
   242  	reqVal, err := JSON.Call("stringify", call.Argument(0))
   243  	if err != nil {
   244  		throwJSException(err.Error())
   245  	}
   246  	var (
   247  		rawReq = reqVal.String()
   248  		dec    = json.NewDecoder(strings.NewReader(rawReq))
   249  		reqs   []jsonrpcCall
   250  		batch  bool
   251  	)
   252  	dec.UseNumber()
   253  	if rawReq[0] == '[' {
   254  		batch = true
   255  		dec.Decode(&reqs)
   256  	} else {
   257  		batch = false
   258  		reqs = make([]jsonrpcCall, 1)
   259  		dec.Decode(&reqs[0])
   260  	}
   261  
   262  	resps, _ := call.Otto.Object("new Array()")
   263  	for _, req := range reqs {
   264  		resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
   265  		resp.Set("id", req.Id)
   266  		var result json.RawMessage
   267  		err = b.client.Call(&result, req.Method, req.Params...)
   268  
   269  		if err == nil {
   270  			if result == nil {
   271  
   272  				resp.Set("result", otto.NullValue())
   273  			} else {
   274  				resultVal, err := JSON.Call("parse", string(result))
   275  				if err != nil {
   276  					setError(resp, -32603, err.Error(), nil)
   277  				} else {
   278  					resp.Set("result", resultVal)
   279  				}
   280  			}
   281  		} else {
   282  			code := -32603
   283  			var data interface{}
   284  			if err, ok := err.(rpc.Error); ok {
   285  				code = err.ErrorCode()
   286  			}
   287  
   288  			if err, ok := err.(rpc.DataError); ok {
   289  				data = err.ErrorData()
   290  			}
   291  
   292  			setError(resp, code, err.Error(), data)
   293  		}
   294  		resps.Call("push", resp)
   295  	}
   296  
   297  	if batch {
   298  		response = resps.Value()
   299  	} else {
   300  		response, _ = resps.Get("0")
   301  	}
   302  	if fn := call.Argument(1); fn.Class() == "Function" {
   303  		fn.Call(otto.NullValue(), otto.NullValue(), response)
   304  		return otto.UndefinedValue()
   305  	}
   306  	return response
   307  }
   308  
   309  func setError(resp *otto.Object, code int, msg string, data interface{}) {
   310  
   311  	err := make(map[string]interface{})
   312  	err["code"] = code
   313  	err["message"] = msg
   314  	if data != nil {
   315  		err["data"] = data
   316  	}
   317  	resp.Set("error", err)
   318  }
   319  
   320  func throwJSException(msg interface{}) otto.Value {
   321  	val, err := otto.ToValue(msg)
   322  	if err != nil {
   323  		log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err)
   324  	}
   325  	panic(val)
   326  }