github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/console/bridge.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:33</date>
    10  //</624342612505006080>
    11  
    12  
    13  package console
    14  
    15  import (
    16  	"encoding/json"
    17  	"fmt"
    18  	"io"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/ethereum/go-ethereum/accounts/usbwallet"
    23  	"github.com/ethereum/go-ethereum/log"
    24  	"github.com/ethereum/go-ethereum/rpc"
    25  	"github.com/robertkrimen/otto"
    26  )
    27  
    28  //Bridge是一组javascript实用程序方法,用于连接.js运行时。
    29  //环境和支持远程方法调用的go-rpc连接。
    30  type bridge struct {
    31  client   *rpc.Client  //通过RPC客户端执行以太坊请求
    32  prompter UserPrompter //输入提示以允许交互式用户反馈
    33  printer  io.Writer    //要将任何显示字符串序列化到的输出编写器
    34  }
    35  
    36  //
    37  func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge {
    38  	return &bridge{
    39  		client:   client,
    40  		prompter: prompter,
    41  		printer:  printer,
    42  	}
    43  }
    44  
    45  //
    46  //不回显密码提示获取密码短语并执行原始密码
    47  //rpc方法(保存在jeth.newaccount中)用于实际执行rpc调用。
    48  func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
    49  	var (
    50  		password string
    51  		confirm  string
    52  		err      error
    53  	)
    54  	switch {
    55  //未指定密码,提示用户输入密码
    56  	case len(call.ArgumentList) == 0:
    57  		if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
    58  			throwJSException(err.Error())
    59  		}
    60  		if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
    61  			throwJSException(err.Error())
    62  		}
    63  		if password != confirm {
    64  			throwJSException("passphrases don't match!")
    65  		}
    66  
    67  //指定了单个字符串密码,请使用
    68  	case len(call.ArgumentList) == 1 && call.Argument(0).IsString():
    69  		password, _ = call.Argument(0).ToString()
    70  
    71  //否则会因某些错误而失败
    72  	default:
    73  		throwJSException("expected 0 or 1 string argument")
    74  	}
    75  //获取密码,执行呼叫并返回
    76  	ret, err := call.Otto.Call("jeth.newAccount", nil, password)
    77  	if err != nil {
    78  		throwJSException(err.Error())
    79  	}
    80  	return ret
    81  }
    82  
    83  //openwallet是一个围绕personal.openwallet的包装,它可以解释和
    84  //对某些错误消息作出反应,例如trezor pin matrix请求。
    85  func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
    86  //确保我们有一个指定要打开的钱包
    87  	if !call.Argument(0).IsString() {
    88  		throwJSException("first argument must be the wallet URL to open")
    89  	}
    90  	wallet := call.Argument(0)
    91  
    92  	var passwd otto.Value
    93  	if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
    94  		passwd, _ = otto.ToValue("")
    95  	} else {
    96  		passwd = call.Argument(1)
    97  	}
    98  //
    99  	val, err := call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
   100  	if err == nil {
   101  		return val
   102  	}
   103  //钱包打开失败,除非是密码输入,否则报告错误
   104  	if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) {
   105  		throwJSException(err.Error())
   106  	}
   107  //请求Trezor管脚矩阵输入,向用户显示矩阵并获取数据
   108  	fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
   109  	fmt.Fprintf(b.printer, "7 | 8 | 9\n")
   110  	fmt.Fprintf(b.printer, "--+---+--\n")
   111  	fmt.Fprintf(b.printer, "4 | 5 | 6\n")
   112  	fmt.Fprintf(b.printer, "--+---+--\n")
   113  	fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
   114  
   115  	if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
   116  		throwJSException(err.Error())
   117  	} else {
   118  		passwd, _ = otto.ToValue(input)
   119  	}
   120  	if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
   121  		throwJSException(err.Error())
   122  	}
   123  	return val
   124  }
   125  
   126  //UnlockAccount是围绕Personal.UnlockAccount RPC方法的包装,
   127  //
   128  //
   129  //RPC调用。
   130  func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
   131  //
   132  	if !call.Argument(0).IsString() {
   133  		throwJSException("first argument must be the account to unlock")
   134  	}
   135  	account := call.Argument(0)
   136  
   137  //如果未给出密码或是空值,提示用户输入密码。
   138  	var passwd otto.Value
   139  
   140  	if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
   141  		fmt.Fprintf(b.printer, "Unlock account %s\n", account)
   142  		if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
   143  			throwJSException(err.Error())
   144  		} else {
   145  			passwd, _ = otto.ToValue(input)
   146  		}
   147  	} else {
   148  		if !call.Argument(1).IsString() {
   149  			throwJSException("password must be a string")
   150  		}
   151  		passwd = call.Argument(1)
   152  	}
   153  //第三个参数是帐户必须解锁的持续时间。
   154  	duration := otto.NullValue()
   155  	if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() {
   156  		if !call.Argument(2).IsNumber() {
   157  			throwJSException("unlock duration must be a number")
   158  		}
   159  		duration = call.Argument(2)
   160  	}
   161  //将请求发送到后端并返回
   162  	val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration)
   163  	if err != nil {
   164  		throwJSException(err.Error())
   165  	}
   166  	return val
   167  }
   168  
   169  //sign是围绕personal.sign rpc方法的包装,该方法使用非回显密码。
   170  //提示获取密码短语并执行原始RPC方法(保存在
   171  //用它来实际执行RPC调用。
   172  func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) {
   173  	var (
   174  		message = call.Argument(0)
   175  		account = call.Argument(1)
   176  		passwd  = call.Argument(2)
   177  	)
   178  
   179  	if !message.IsString() {
   180  		throwJSException("first argument must be the message to sign")
   181  	}
   182  	if !account.IsString() {
   183  		throwJSException("second argument must be the account to sign with")
   184  	}
   185  
   186  //如果未提供密码或密码为空,请询问用户并确保密码为字符串
   187  	if passwd.IsUndefined() || passwd.IsNull() {
   188  		fmt.Fprintf(b.printer, "Give password for account %s\n", account)
   189  		if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
   190  			throwJSException(err.Error())
   191  		} else {
   192  			passwd, _ = otto.ToValue(input)
   193  		}
   194  	}
   195  	if !passwd.IsString() {
   196  		throwJSException("third argument must be the password to unlock the account")
   197  	}
   198  
   199  //将请求发送到后端并返回
   200  	val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd)
   201  	if err != nil {
   202  		throwJSException(err.Error())
   203  	}
   204  	return val
   205  }
   206  
   207  //睡眠将在指定的秒数内阻止控制台。
   208  func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) {
   209  	if call.Argument(0).IsNumber() {
   210  		sleep, _ := call.Argument(0).ToInteger()
   211  		time.Sleep(time.Duration(sleep) * time.Second)
   212  		return otto.TrueValue()
   213  	}
   214  	return throwJSException("usage: sleep(<number of seconds>)")
   215  }
   216  
   217  //SleepBlocks将为指定数量的新块(可选)阻止控制台
   218  //
   219  func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
   220  	var (
   221  		blocks = int64(0)
   222  sleep  = int64(9999999999999999) //无限期地
   223  	)
   224  //分析睡眠的输入参数
   225  	nArgs := len(call.ArgumentList)
   226  	if nArgs == 0 {
   227  		throwJSException("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
   228  	}
   229  	if nArgs >= 1 {
   230  		if call.Argument(0).IsNumber() {
   231  			blocks, _ = call.Argument(0).ToInteger()
   232  		} else {
   233  			throwJSException("expected number as first argument")
   234  		}
   235  	}
   236  	if nArgs >= 2 {
   237  		if call.Argument(1).IsNumber() {
   238  			sleep, _ = call.Argument(1).ToInteger()
   239  		} else {
   240  			throwJSException("expected number as second argument")
   241  		}
   242  	}
   243  //通过控制台,这将允许Web3调用适当的
   244  //如果收到延迟的响应或通知,则进行回调。
   245  	blockNumber := func() int64 {
   246  		result, err := call.Otto.Run("eth.blockNumber")
   247  		if err != nil {
   248  			throwJSException(err.Error())
   249  		}
   250  		block, err := result.ToInteger()
   251  		if err != nil {
   252  			throwJSException(err.Error())
   253  		}
   254  		return block
   255  	}
   256  //轮询当前块号,直到超时为止
   257  	targetBlockNr := blockNumber() + blocks
   258  	deadline := time.Now().Add(time.Duration(sleep) * time.Second)
   259  
   260  	for time.Now().Before(deadline) {
   261  		if blockNumber() >= targetBlockNr {
   262  			return otto.TrueValue()
   263  		}
   264  		time.Sleep(time.Second)
   265  	}
   266  	return otto.FalseValue()
   267  }
   268  
   269  type jsonrpcCall struct {
   270  	ID     int64
   271  	Method string
   272  	Params []interface{}
   273  }
   274  
   275  //send实现Web3提供程序“send”方法。
   276  func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
   277  //将请求改为go值。
   278  	JSON, _ := call.Otto.Object("JSON")
   279  	reqVal, err := JSON.Call("stringify", call.Argument(0))
   280  	if err != nil {
   281  		throwJSException(err.Error())
   282  	}
   283  	var (
   284  		rawReq = reqVal.String()
   285  		dec    = json.NewDecoder(strings.NewReader(rawReq))
   286  		reqs   []jsonrpcCall
   287  		batch  bool
   288  	)
   289  dec.UseNumber() //避免浮标64
   290  	if rawReq[0] == '[' {
   291  		batch = true
   292  		dec.Decode(&reqs)
   293  	} else {
   294  		batch = false
   295  		reqs = make([]jsonrpcCall, 1)
   296  		dec.Decode(&reqs[0])
   297  	}
   298  
   299  //执行请求。
   300  	resps, _ := call.Otto.Object("new Array()")
   301  	for _, req := range reqs {
   302  		resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
   303  		resp.Set("id", req.ID)
   304  		var result json.RawMessage
   305  		err = b.client.Call(&result, req.Method, req.Params...)
   306  		switch err := err.(type) {
   307  		case nil:
   308  			if result == nil {
   309  //特殊情况为空,因为它被解码为空
   310  //出于某种原因,原始消息。
   311  				resp.Set("result", otto.NullValue())
   312  			} else {
   313  				resultVal, err := JSON.Call("parse", string(result))
   314  				if err != nil {
   315  					setError(resp, -32603, err.Error())
   316  				} else {
   317  					resp.Set("result", resultVal)
   318  				}
   319  			}
   320  		case rpc.Error:
   321  			setError(resp, err.ErrorCode(), err.Error())
   322  		default:
   323  			setError(resp, -32603, err.Error())
   324  		}
   325  		resps.Call("push", resp)
   326  	}
   327  
   328  //返回对回调的响应(如果提供)
   329  //
   330  	if batch {
   331  		response = resps.Value()
   332  	} else {
   333  		response, _ = resps.Get("0")
   334  	}
   335  	if fn := call.Argument(1); fn.Class() == "Function" {
   336  		fn.Call(otto.NullValue(), otto.NullValue(), response)
   337  		return otto.UndefinedValue()
   338  	}
   339  	return response
   340  }
   341  
   342  func setError(resp *otto.Object, code int, msg string) {
   343  	resp.Set("error", map[string]interface{}{"code": code, "message": msg})
   344  }
   345  
   346  //throwjsException在otto.value上崩溃。奥托虚拟机将从
   347  //惊慌失措,将msg作为一个javascript错误抛出。
   348  func throwJSException(msg interface{}) otto.Value {
   349  	val, err := otto.ToValue(msg)
   350  	if err != nil {
   351  		log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err)
   352  	}
   353  	panic(val)
   354  }
   355