bridge.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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. } else {
  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. }
  147. case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()):
  148. // PIN unblock requested, fetch PUK and new PIN from the user
  149. var pukpin string
  150. input, err := b.prompter.PromptPassword("Please enter current PUK: ")
  151. if err != nil {
  152. return nil, err
  153. }
  154. pukpin = input
  155. input, err = b.prompter.PromptPassword("Please enter new PIN: ")
  156. if err != nil {
  157. return nil, err
  158. }
  159. pukpin += input
  160. if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(pukpin)); err != nil {
  161. return nil, err
  162. }
  163. case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()):
  164. // PIN input requested, fetch from the user and call open again
  165. input, err := b.prompter.PromptPassword("Please enter current PIN: ")
  166. if err != nil {
  167. return nil, err
  168. }
  169. if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil {
  170. return nil, err
  171. }
  172. default:
  173. // Unknown error occurred, drop to the user
  174. return nil, err
  175. }
  176. return val, nil
  177. }
  178. func (b *bridge) readPassphraseAndReopenWallet(call jsre.Call) (goja.Value, error) {
  179. wallet := call.Argument(0)
  180. input, err := b.prompter.PromptPassword("Please enter your passphrase: ")
  181. if err != nil {
  182. return nil, err
  183. }
  184. openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
  185. if !callable {
  186. return nil, fmt.Errorf("jeth.openWallet is not callable")
  187. }
  188. return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
  189. }
  190. func (b *bridge) readPinAndReopenWallet(call jsre.Call) (goja.Value, error) {
  191. wallet := call.Argument(0)
  192. // Trezor PIN matrix input requested, display the matrix to the user and fetch the data
  193. fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
  194. fmt.Fprintf(b.printer, "7 | 8 | 9\n")
  195. fmt.Fprintf(b.printer, "--+---+--\n")
  196. fmt.Fprintf(b.printer, "4 | 5 | 6\n")
  197. fmt.Fprintf(b.printer, "--+---+--\n")
  198. fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
  199. input, err := b.prompter.PromptPassword("Please enter current PIN: ")
  200. if err != nil {
  201. return nil, err
  202. }
  203. openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
  204. if !callable {
  205. return nil, fmt.Errorf("jeth.openWallet is not callable")
  206. }
  207. return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
  208. }
  209. // UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
  210. // uses a non-echoing password prompt to acquire the passphrase and executes the
  211. // original RPC method (saved in jeth.unlockAccount) with it to actually execute
  212. // the RPC call.
  213. func (b *bridge) UnlockAccount(call jsre.Call) (goja.Value, error) {
  214. if len(call.Arguments) < 1 {
  215. return nil, fmt.Errorf("usage: unlockAccount(account, [ password, duration ])")
  216. }
  217. account := call.Argument(0)
  218. // Make sure we have an account specified to unlock.
  219. if goja.IsUndefined(account) || goja.IsNull(account) || account.ExportType().Kind() != reflect.String {
  220. return nil, fmt.Errorf("first argument must be the account to unlock")
  221. }
  222. // If password is not given or is the null value, prompt the user for it.
  223. var passwd goja.Value
  224. if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
  225. fmt.Fprintf(b.printer, "Unlock account %s\n", account)
  226. input, err := b.prompter.PromptPassword("Passphrase: ")
  227. if err != nil {
  228. return nil, err
  229. }
  230. passwd = call.VM.ToValue(input)
  231. } else {
  232. if call.Argument(1).ExportType().Kind() != reflect.String {
  233. return nil, fmt.Errorf("password must be a string")
  234. }
  235. passwd = call.Argument(1)
  236. }
  237. // Third argument is the duration how long the account should be unlocked.
  238. duration := goja.Null()
  239. if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
  240. if !isNumber(call.Argument(2)) {
  241. return nil, fmt.Errorf("unlock duration must be a number")
  242. }
  243. duration = call.Argument(2)
  244. }
  245. // Send the request to the backend and return.
  246. unlockAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
  247. if !callable {
  248. return nil, fmt.Errorf("jeth.unlockAccount is not callable")
  249. }
  250. return unlockAccount(goja.Null(), account, passwd, duration)
  251. }
  252. // Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
  253. // prompt to acquire the passphrase and executes the original RPC method (saved in
  254. // jeth.sign) with it to actually execute the RPC call.
  255. func (b *bridge) Sign(call jsre.Call) (goja.Value, error) {
  256. if nArgs := len(call.Arguments); nArgs < 2 {
  257. return nil, fmt.Errorf("usage: sign(message, account, [ password ])")
  258. }
  259. var (
  260. message = call.Argument(0)
  261. account = call.Argument(1)
  262. passwd = call.Argument(2)
  263. )
  264. if goja.IsUndefined(message) || message.ExportType().Kind() != reflect.String {
  265. return nil, fmt.Errorf("first argument must be the message to sign")
  266. }
  267. if goja.IsUndefined(account) || account.ExportType().Kind() != reflect.String {
  268. return nil, fmt.Errorf("second argument must be the account to sign with")
  269. }
  270. // if the password is not given or null ask the user and ensure password is a string
  271. if goja.IsUndefined(passwd) || goja.IsNull(passwd) {
  272. fmt.Fprintf(b.printer, "Give password for account %s\n", account)
  273. input, err := b.prompter.PromptPassword("Password: ")
  274. if err != nil {
  275. return nil, err
  276. }
  277. passwd = call.VM.ToValue(input)
  278. } else if passwd.ExportType().Kind() != reflect.String {
  279. return nil, fmt.Errorf("third argument must be the password to unlock the account")
  280. }
  281. // Send the request to the backend and return
  282. sign, callable := goja.AssertFunction(getJeth(call.VM).Get("sign"))
  283. if !callable {
  284. return nil, fmt.Errorf("jeth.sign is not callable")
  285. }
  286. return sign(goja.Null(), message, account, passwd)
  287. }
  288. // Sleep will block the console for the specified number of seconds.
  289. func (b *bridge) Sleep(call jsre.Call) (goja.Value, error) {
  290. if nArgs := len(call.Arguments); nArgs < 1 {
  291. return nil, fmt.Errorf("usage: sleep(<number of seconds>)")
  292. }
  293. sleepObj := call.Argument(0)
  294. if goja.IsUndefined(sleepObj) || goja.IsNull(sleepObj) || !isNumber(sleepObj) {
  295. return nil, fmt.Errorf("usage: sleep(<number of seconds>)")
  296. }
  297. sleep := sleepObj.ToFloat()
  298. time.Sleep(time.Duration(sleep * float64(time.Second)))
  299. return call.VM.ToValue(true), nil
  300. }
  301. // SleepBlocks will block the console for a specified number of new blocks optionally
  302. // until the given timeout is reached.
  303. func (b *bridge) SleepBlocks(call jsre.Call) (goja.Value, error) {
  304. // Parse the input parameters for the sleep.
  305. var (
  306. blocks = int64(0)
  307. sleep = int64(9999999999999999) // indefinitely
  308. )
  309. nArgs := len(call.Arguments)
  310. if nArgs == 0 {
  311. return nil, fmt.Errorf("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
  312. }
  313. if nArgs >= 1 {
  314. if goja.IsNull(call.Argument(0)) || goja.IsUndefined(call.Argument(0)) || !isNumber(call.Argument(0)) {
  315. return nil, fmt.Errorf("expected number as first argument")
  316. }
  317. blocks = call.Argument(0).ToInteger()
  318. }
  319. if nArgs >= 2 {
  320. if goja.IsNull(call.Argument(1)) || goja.IsUndefined(call.Argument(1)) || !isNumber(call.Argument(1)) {
  321. return nil, fmt.Errorf("expected number as second argument")
  322. }
  323. sleep = call.Argument(1).ToInteger()
  324. }
  325. // Poll the current block number until either it or a timeout is reached.
  326. deadline := time.Now().Add(time.Duration(sleep) * time.Second)
  327. var lastNumber hexutil.Uint64
  328. if err := b.client.Call(&lastNumber, "eth_blockNumber"); err != nil {
  329. return nil, err
  330. }
  331. for time.Now().Before(deadline) {
  332. var number hexutil.Uint64
  333. if err := b.client.Call(&number, "eth_blockNumber"); err != nil {
  334. return nil, err
  335. }
  336. if number != lastNumber {
  337. lastNumber = number
  338. blocks--
  339. }
  340. if blocks <= 0 {
  341. break
  342. }
  343. time.Sleep(time.Second)
  344. }
  345. return call.VM.ToValue(true), nil
  346. }
  347. type jsonrpcCall struct {
  348. ID int64
  349. Method string
  350. Params []interface{}
  351. }
  352. // Send implements the web3 provider "send" method.
  353. func (b *bridge) Send(call jsre.Call) (goja.Value, error) {
  354. // Remarshal the request into a Go value.
  355. reqVal, err := call.Argument(0).ToObject(call.VM).MarshalJSON()
  356. if err != nil {
  357. return nil, err
  358. }
  359. var (
  360. rawReq = string(reqVal)
  361. dec = json.NewDecoder(strings.NewReader(rawReq))
  362. reqs []jsonrpcCall
  363. batch bool
  364. )
  365. dec.UseNumber() // avoid float64s
  366. if rawReq[0] == '[' {
  367. batch = true
  368. dec.Decode(&reqs)
  369. } else {
  370. batch = false
  371. reqs = make([]jsonrpcCall, 1)
  372. dec.Decode(&reqs[0])
  373. }
  374. // Execute the requests.
  375. var resps []*goja.Object
  376. for _, req := range reqs {
  377. resp := call.VM.NewObject()
  378. resp.Set("jsonrpc", "2.0")
  379. resp.Set("id", req.ID)
  380. var result json.RawMessage
  381. if err = b.client.Call(&result, req.Method, req.Params...); err == nil {
  382. if result == nil {
  383. // Special case null because it is decoded as an empty
  384. // raw message for some reason.
  385. resp.Set("result", goja.Null())
  386. } else {
  387. JSON := call.VM.Get("JSON").ToObject(call.VM)
  388. parse, callable := goja.AssertFunction(JSON.Get("parse"))
  389. if !callable {
  390. return nil, fmt.Errorf("JSON.parse is not a function")
  391. }
  392. resultVal, err := parse(goja.Null(), call.VM.ToValue(string(result)))
  393. if err != nil {
  394. setError(resp, -32603, err.Error(), nil)
  395. } else {
  396. resp.Set("result", resultVal)
  397. }
  398. }
  399. } else {
  400. code := -32603
  401. var data interface{}
  402. if err, ok := err.(rpc.Error); ok {
  403. code = err.ErrorCode()
  404. }
  405. if err, ok := err.(rpc.DataError); ok {
  406. data = err.ErrorData()
  407. }
  408. setError(resp, code, err.Error(), data)
  409. }
  410. resps = append(resps, resp)
  411. }
  412. // Return the responses either to the callback (if supplied)
  413. // or directly as the return value.
  414. var result goja.Value
  415. if batch {
  416. result = call.VM.ToValue(resps)
  417. } else {
  418. result = resps[0]
  419. }
  420. if fn, isFunc := goja.AssertFunction(call.Argument(1)); isFunc {
  421. fn(goja.Null(), goja.Null(), result)
  422. return goja.Undefined(), nil
  423. }
  424. return result, nil
  425. }
  426. func setError(resp *goja.Object, code int, msg string, data interface{}) {
  427. err := make(map[string]interface{})
  428. err["code"] = code
  429. err["message"] = msg
  430. if data != nil {
  431. err["data"] = data
  432. }
  433. resp.Set("error", err)
  434. }
  435. // isNumber returns true if input value is a JS number.
  436. func isNumber(v goja.Value) bool {
  437. k := v.ExportType().Kind()
  438. return k >= reflect.Int && k <= reflect.Float64
  439. }
  440. func getObject(vm *goja.Runtime, name string) *goja.Object {
  441. v := vm.Get(name)
  442. if v == nil {
  443. return nil
  444. }
  445. return v.ToObject(vm)
  446. }