bridge.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package console
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "io"
  21. "reflect"
  22. "strings"
  23. "time"
  24. "github.com/dop251/goja"
  25. "github.com/ethereum/go-ethereum/accounts/scwallet"
  26. "github.com/ethereum/go-ethereum/accounts/usbwallet"
  27. "github.com/ethereum/go-ethereum/common/hexutil"
  28. "github.com/ethereum/go-ethereum/console/prompt"
  29. "github.com/ethereum/go-ethereum/internal/jsre"
  30. "github.com/ethereum/go-ethereum/rpc"
  31. )
  32. // bridge is a collection of JavaScript utility methods to bride the .js runtime
  33. // environment and the Go RPC connection backing the remote method calls.
  34. type bridge struct {
  35. client *rpc.Client // RPC client to execute Ethereum requests through
  36. prompter prompt.UserPrompter // Input prompter to allow interactive user feedback
  37. printer io.Writer // Output writer to serialize any display strings to
  38. }
  39. // newBridge creates a new JavaScript wrapper around an RPC client.
  40. func newBridge(client *rpc.Client, prompter prompt.UserPrompter, printer io.Writer) *bridge {
  41. return &bridge{
  42. client: client,
  43. prompter: prompter,
  44. printer: printer,
  45. }
  46. }
  47. func getJeth(vm *goja.Runtime) *goja.Object {
  48. jeth := vm.Get("jeth")
  49. if jeth == nil {
  50. panic(vm.ToValue("jeth object does not exist"))
  51. }
  52. return jeth.ToObject(vm)
  53. }
  54. // NewAccount is a wrapper around the personal.newAccount RPC method that uses a
  55. // non-echoing password prompt to acquire the passphrase and executes the original
  56. // RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
  57. func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) {
  58. var (
  59. password string
  60. confirm string
  61. err error
  62. )
  63. switch {
  64. // No password was specified, prompt the user for it
  65. case len(call.Arguments) == 0:
  66. if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
  67. return nil, err
  68. }
  69. if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
  70. return nil, err
  71. }
  72. if password != confirm {
  73. return nil, fmt.Errorf("passwords don't match!")
  74. }
  75. // A single string password was specified, use that
  76. case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
  77. password = call.Argument(0).ToString().String()
  78. default:
  79. return nil, fmt.Errorf("expected 0 or 1 string argument")
  80. }
  81. // Password acquired, execute the call and return
  82. newAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("newAccount"))
  83. if !callable {
  84. return nil, fmt.Errorf("jeth.newAccount is not callable")
  85. }
  86. ret, err := newAccount(goja.Null(), call.VM.ToValue(password))
  87. if err != nil {
  88. return nil, err
  89. }
  90. return ret, nil
  91. }
  92. // OpenWallet is a wrapper around personal.openWallet which can interpret and
  93. // react to certain error messages, such as the Trezor PIN matrix request.
  94. func (b *bridge) OpenWallet(call jsre.Call) (goja.Value, error) {
  95. // Make sure we have a wallet specified to open
  96. if call.Argument(0).ToObject(call.VM).ClassName() != "String" {
  97. return nil, fmt.Errorf("first argument must be the wallet URL to open")
  98. }
  99. wallet := call.Argument(0)
  100. var passwd goja.Value
  101. if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
  102. passwd = call.VM.ToValue("")
  103. } else {
  104. passwd = call.Argument(1)
  105. }
  106. // Open the wallet and return if successful in itself
  107. openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
  108. if !callable {
  109. return nil, fmt.Errorf("jeth.openWallet is not callable")
  110. }
  111. val, err := openWallet(goja.Null(), wallet, passwd)
  112. if err == nil {
  113. return val, nil
  114. }
  115. // Wallet open failed, report error unless it's a PIN or PUK entry
  116. switch {
  117. case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()):
  118. val, err = b.readPinAndReopenWallet(call)
  119. if err == nil {
  120. return val, nil
  121. }
  122. val, err = b.readPassphraseAndReopenWallet(call)
  123. if err != nil {
  124. return nil, err
  125. }
  126. case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()):
  127. // PUK input requested, fetch from the user and call open again
  128. input, err := b.prompter.PromptPassword("Please enter the pairing password: ")
  129. if err != nil {
  130. return nil, err
  131. }
  132. passwd = call.VM.ToValue(input)
  133. if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
  134. if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) {
  135. return nil, err
  136. }
  137. // PIN input requested, fetch from the user and call open again
  138. input, err := b.prompter.PromptPassword("Please enter current PIN: ")
  139. if err != nil {
  140. return nil, err
  141. }
  142. if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil {
  143. return nil, err
  144. }
  145. }
  146. case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()):
  147. // PIN unblock requested, fetch PUK and new PIN from the user
  148. var pukpin string
  149. input, err := b.prompter.PromptPassword("Please enter current PUK: ")
  150. if err != nil {
  151. return nil, err
  152. }
  153. pukpin = input
  154. input, err = b.prompter.PromptPassword("Please enter new PIN: ")
  155. if err != nil {
  156. return nil, err
  157. }
  158. pukpin += input
  159. if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(pukpin)); err != nil {
  160. return nil, err
  161. }
  162. case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()):
  163. // PIN input requested, fetch from the user and call open again
  164. input, err := b.prompter.PromptPassword("Please enter current PIN: ")
  165. if err != nil {
  166. return nil, err
  167. }
  168. if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil {
  169. return nil, err
  170. }
  171. default:
  172. // Unknown error occurred, drop to the user
  173. return nil, err
  174. }
  175. return val, nil
  176. }
  177. func (b *bridge) readPassphraseAndReopenWallet(call jsre.Call) (goja.Value, error) {
  178. wallet := call.Argument(0)
  179. input, err := b.prompter.PromptPassword("Please enter your passphrase: ")
  180. if err != nil {
  181. return nil, err
  182. }
  183. openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
  184. if !callable {
  185. return nil, fmt.Errorf("jeth.openWallet is not callable")
  186. }
  187. return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
  188. }
  189. func (b *bridge) readPinAndReopenWallet(call jsre.Call) (goja.Value, error) {
  190. wallet := call.Argument(0)
  191. // Trezor PIN matrix input requested, display the matrix to the user and fetch the data
  192. fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
  193. fmt.Fprintf(b.printer, "7 | 8 | 9\n")
  194. fmt.Fprintf(b.printer, "--+---+--\n")
  195. fmt.Fprintf(b.printer, "4 | 5 | 6\n")
  196. fmt.Fprintf(b.printer, "--+---+--\n")
  197. fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
  198. input, err := b.prompter.PromptPassword("Please enter current PIN: ")
  199. if err != nil {
  200. return nil, err
  201. }
  202. openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
  203. if !callable {
  204. return nil, fmt.Errorf("jeth.openWallet is not callable")
  205. }
  206. return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
  207. }
  208. // UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
  209. // uses a non-echoing password prompt to acquire the passphrase and executes the
  210. // original RPC method (saved in jeth.unlockAccount) with it to actually execute
  211. // the RPC call.
  212. func (b *bridge) UnlockAccount(call jsre.Call) (goja.Value, error) {
  213. if len(call.Arguments) < 1 {
  214. return nil, fmt.Errorf("usage: unlockAccount(account, [ password, duration ])")
  215. }
  216. account := call.Argument(0)
  217. // Make sure we have an account specified to unlock.
  218. if goja.IsUndefined(account) || goja.IsNull(account) || account.ExportType().Kind() != reflect.String {
  219. return nil, fmt.Errorf("first argument must be the account to unlock")
  220. }
  221. // If password is not given or is the null value, prompt the user for it.
  222. var passwd goja.Value
  223. if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
  224. fmt.Fprintf(b.printer, "Unlock account %s\n", account)
  225. input, err := b.prompter.PromptPassword("Passphrase: ")
  226. if err != nil {
  227. return nil, err
  228. }
  229. passwd = call.VM.ToValue(input)
  230. } else {
  231. if call.Argument(1).ExportType().Kind() != reflect.String {
  232. return nil, fmt.Errorf("password must be a string")
  233. }
  234. passwd = call.Argument(1)
  235. }
  236. // Third argument is the duration how long the account should be unlocked.
  237. duration := goja.Null()
  238. if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
  239. if !isNumber(call.Argument(2)) {
  240. return nil, fmt.Errorf("unlock duration must be a number")
  241. }
  242. duration = call.Argument(2)
  243. }
  244. // Send the request to the backend and return.
  245. unlockAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
  246. if !callable {
  247. return nil, fmt.Errorf("jeth.unlockAccount is not callable")
  248. }
  249. return unlockAccount(goja.Null(), account, passwd, duration)
  250. }
  251. // Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
  252. // prompt to acquire the passphrase and executes the original RPC method (saved in
  253. // jeth.sign) with it to actually execute the RPC call.
  254. func (b *bridge) Sign(call jsre.Call) (goja.Value, error) {
  255. if nArgs := len(call.Arguments); nArgs < 2 {
  256. return nil, fmt.Errorf("usage: sign(message, account, [ password ])")
  257. }
  258. var (
  259. message = call.Argument(0)
  260. account = call.Argument(1)
  261. passwd = call.Argument(2)
  262. )
  263. if goja.IsUndefined(message) || message.ExportType().Kind() != reflect.String {
  264. return nil, fmt.Errorf("first argument must be the message to sign")
  265. }
  266. if goja.IsUndefined(account) || account.ExportType().Kind() != reflect.String {
  267. return nil, fmt.Errorf("second argument must be the account to sign with")
  268. }
  269. // if the password is not given or null ask the user and ensure password is a string
  270. if goja.IsUndefined(passwd) || goja.IsNull(passwd) {
  271. fmt.Fprintf(b.printer, "Give password for account %s\n", account)
  272. input, err := b.prompter.PromptPassword("Password: ")
  273. if err != nil {
  274. return nil, err
  275. }
  276. passwd = call.VM.ToValue(input)
  277. } else if passwd.ExportType().Kind() != reflect.String {
  278. return nil, fmt.Errorf("third argument must be the password to unlock the account")
  279. }
  280. // Send the request to the backend and return
  281. sign, callable := goja.AssertFunction(getJeth(call.VM).Get("sign"))
  282. if !callable {
  283. return nil, fmt.Errorf("jeth.sign is not callable")
  284. }
  285. return sign(goja.Null(), message, account, passwd)
  286. }
  287. // Sleep will block the console for the specified number of seconds.
  288. func (b *bridge) Sleep(call jsre.Call) (goja.Value, error) {
  289. if nArgs := len(call.Arguments); nArgs < 1 {
  290. return nil, fmt.Errorf("usage: sleep(<number of seconds>)")
  291. }
  292. sleepObj := call.Argument(0)
  293. if goja.IsUndefined(sleepObj) || goja.IsNull(sleepObj) || !isNumber(sleepObj) {
  294. return nil, fmt.Errorf("usage: sleep(<number of seconds>)")
  295. }
  296. sleep := sleepObj.ToFloat()
  297. time.Sleep(time.Duration(sleep * float64(time.Second)))
  298. return call.VM.ToValue(true), nil
  299. }
  300. // SleepBlocks will block the console for a specified number of new blocks optionally
  301. // until the given timeout is reached.
  302. func (b *bridge) SleepBlocks(call jsre.Call) (goja.Value, error) {
  303. // Parse the input parameters for the sleep.
  304. var (
  305. blocks = int64(0)
  306. sleep = int64(9999999999999999) // indefinitely
  307. )
  308. nArgs := len(call.Arguments)
  309. if nArgs == 0 {
  310. return nil, fmt.Errorf("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
  311. }
  312. if nArgs >= 1 {
  313. if goja.IsNull(call.Argument(0)) || goja.IsUndefined(call.Argument(0)) || !isNumber(call.Argument(0)) {
  314. return nil, fmt.Errorf("expected number as first argument")
  315. }
  316. blocks = call.Argument(0).ToInteger()
  317. }
  318. if nArgs >= 2 {
  319. if goja.IsNull(call.Argument(1)) || goja.IsUndefined(call.Argument(1)) || !isNumber(call.Argument(1)) {
  320. return nil, fmt.Errorf("expected number as second argument")
  321. }
  322. sleep = call.Argument(1).ToInteger()
  323. }
  324. // Poll the current block number until either it or a timeout is reached.
  325. deadline := time.Now().Add(time.Duration(sleep) * time.Second)
  326. var lastNumber hexutil.Uint64
  327. if err := b.client.Call(&lastNumber, "eth_blockNumber"); err != nil {
  328. return nil, err
  329. }
  330. for time.Now().Before(deadline) {
  331. var number hexutil.Uint64
  332. if err := b.client.Call(&number, "eth_blockNumber"); err != nil {
  333. return nil, err
  334. }
  335. if number != lastNumber {
  336. lastNumber = number
  337. blocks--
  338. }
  339. if blocks <= 0 {
  340. break
  341. }
  342. time.Sleep(time.Second)
  343. }
  344. return call.VM.ToValue(true), nil
  345. }
  346. type jsonrpcCall struct {
  347. ID int64
  348. Method string
  349. Params []interface{}
  350. }
  351. // Send implements the web3 provider "send" method.
  352. func (b *bridge) Send(call jsre.Call) (goja.Value, error) {
  353. // Remarshal the request into a Go value.
  354. reqVal, err := call.Argument(0).ToObject(call.VM).MarshalJSON()
  355. if err != nil {
  356. return nil, err
  357. }
  358. var (
  359. rawReq = string(reqVal)
  360. dec = json.NewDecoder(strings.NewReader(rawReq))
  361. reqs []jsonrpcCall
  362. batch bool
  363. )
  364. dec.UseNumber() // avoid float64s
  365. if rawReq[0] == '[' {
  366. batch = true
  367. dec.Decode(&reqs)
  368. } else {
  369. batch = false
  370. reqs = make([]jsonrpcCall, 1)
  371. dec.Decode(&reqs[0])
  372. }
  373. // Execute the requests.
  374. var resps []*goja.Object
  375. for _, req := range reqs {
  376. resp := call.VM.NewObject()
  377. resp.Set("jsonrpc", "2.0")
  378. resp.Set("id", req.ID)
  379. var result json.RawMessage
  380. if err = b.client.Call(&result, req.Method, req.Params...); err == nil {
  381. if result == nil {
  382. // Special case null because it is decoded as an empty
  383. // raw message for some reason.
  384. resp.Set("result", goja.Null())
  385. } else {
  386. JSON := call.VM.Get("JSON").ToObject(call.VM)
  387. parse, callable := goja.AssertFunction(JSON.Get("parse"))
  388. if !callable {
  389. return nil, fmt.Errorf("JSON.parse is not a function")
  390. }
  391. resultVal, err := parse(goja.Null(), call.VM.ToValue(string(result)))
  392. if err != nil {
  393. setError(resp, -32603, err.Error(), nil)
  394. } else {
  395. resp.Set("result", resultVal)
  396. }
  397. }
  398. } else {
  399. code := -32603
  400. var data interface{}
  401. if err, ok := err.(rpc.Error); ok {
  402. code = err.ErrorCode()
  403. }
  404. if err, ok := err.(rpc.DataError); ok {
  405. data = err.ErrorData()
  406. }
  407. setError(resp, code, err.Error(), data)
  408. }
  409. resps = append(resps, resp)
  410. }
  411. // Return the responses either to the callback (if supplied)
  412. // or directly as the return value.
  413. var result goja.Value
  414. if batch {
  415. result = call.VM.ToValue(resps)
  416. } else {
  417. result = resps[0]
  418. }
  419. if fn, isFunc := goja.AssertFunction(call.Argument(1)); isFunc {
  420. fn(goja.Null(), goja.Null(), result)
  421. return goja.Undefined(), nil
  422. }
  423. return result, nil
  424. }
  425. func setError(resp *goja.Object, code int, msg string, data interface{}) {
  426. err := make(map[string]interface{})
  427. err["code"] = code
  428. err["message"] = msg
  429. if data != nil {
  430. err["data"] = data
  431. }
  432. resp.Set("error", err)
  433. }
  434. // isNumber returns true if input value is a JS number.
  435. func isNumber(v goja.Value) bool {
  436. k := v.ExportType().Kind()
  437. return k >= reflect.Int && k <= reflect.Float64
  438. }
  439. func getObject(vm *goja.Runtime, name string) *goja.Object {
  440. v := vm.Get(name)
  441. if v == nil {
  442. return nil
  443. }
  444. return v.ToObject(vm)
  445. }