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