api.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /*
  2. For each request type, define the following:
  3. 1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder()
  4. 2. json.Decoder() calls "UnmarshalON" defined on each "Args" struct
  5. 3. EthereumApi method, taking the "Args" type and replying with an interface to be marshalled to ON
  6. */
  7. package rpc
  8. import (
  9. "fmt"
  10. "math/big"
  11. "strings"
  12. "sync"
  13. "time"
  14. "github.com/ethereum/go-ethereum/core"
  15. "github.com/ethereum/go-ethereum/core/types"
  16. "github.com/ethereum/go-ethereum/crypto"
  17. "github.com/ethereum/go-ethereum/ethdb"
  18. "github.com/ethereum/go-ethereum/ethutil"
  19. "github.com/ethereum/go-ethereum/event"
  20. "github.com/ethereum/go-ethereum/event/filter"
  21. "github.com/ethereum/go-ethereum/state"
  22. "github.com/ethereum/go-ethereum/ui"
  23. "github.com/ethereum/go-ethereum/xeth"
  24. )
  25. var (
  26. defaultGasPrice = big.NewInt(10000000000000)
  27. defaultGas = big.NewInt(10000)
  28. filterTickerTime = 15 * time.Second
  29. )
  30. type EthereumApi struct {
  31. eth *xeth.XEth
  32. xethMu sync.RWMutex
  33. mux *event.TypeMux
  34. quit chan struct{}
  35. filterManager *filter.FilterManager
  36. logMut sync.RWMutex
  37. logs map[int]*logFilter
  38. messagesMut sync.RWMutex
  39. messages map[int]*whisperFilter
  40. // Register keeps a list of accounts and transaction data
  41. regmut sync.Mutex
  42. register map[string][]*NewTxArgs
  43. db ethutil.Database
  44. defaultBlockAge int64
  45. }
  46. func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
  47. db, _ := ethdb.NewLDBDatabase("dapps")
  48. api := &EthereumApi{
  49. eth: eth,
  50. mux: eth.Backend().EventMux(),
  51. quit: make(chan struct{}),
  52. filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
  53. logs: make(map[int]*logFilter),
  54. messages: make(map[int]*whisperFilter),
  55. db: db,
  56. defaultBlockAge: -1,
  57. }
  58. go api.filterManager.Start()
  59. go api.start()
  60. return api
  61. }
  62. func (self *EthereumApi) setStateByBlockNumber(num int64) {
  63. chain := self.xeth().Backend().ChainManager()
  64. var block *types.Block
  65. if self.defaultBlockAge < 0 {
  66. num = chain.CurrentBlock().Number().Int64() + num + 1
  67. }
  68. block = chain.GetBlockByNumber(uint64(num))
  69. if block != nil {
  70. self.useState(state.New(block.Root(), self.xeth().Backend().StateDb()))
  71. } else {
  72. self.useState(chain.State())
  73. }
  74. }
  75. func (self *EthereumApi) start() {
  76. timer := time.NewTicker(filterTickerTime)
  77. events := self.mux.Subscribe(core.ChainEvent{})
  78. done:
  79. for {
  80. select {
  81. case ev := <-events.Chan():
  82. switch ev.(type) {
  83. case core.ChainEvent:
  84. if self.defaultBlockAge < 0 {
  85. self.setStateByBlockNumber(self.defaultBlockAge)
  86. }
  87. }
  88. case <-timer.C:
  89. self.logMut.Lock()
  90. self.messagesMut.Lock()
  91. for id, filter := range self.logs {
  92. if time.Since(filter.timeout) > 20*time.Second {
  93. self.filterManager.UninstallFilter(id)
  94. delete(self.logs, id)
  95. }
  96. }
  97. for id, filter := range self.messages {
  98. if time.Since(filter.timeout) > 20*time.Second {
  99. self.xeth().Whisper().Unwatch(id)
  100. delete(self.messages, id)
  101. }
  102. }
  103. self.logMut.Unlock()
  104. self.messagesMut.Unlock()
  105. case <-self.quit:
  106. break done
  107. }
  108. }
  109. }
  110. func (self *EthereumApi) stop() {
  111. close(self.quit)
  112. }
  113. func (self *EthereumApi) Register(args string, reply *interface{}) error {
  114. self.regmut.Lock()
  115. defer self.regmut.Unlock()
  116. if _, ok := self.register[args]; ok {
  117. self.register[args] = nil // register with empty
  118. }
  119. return nil
  120. }
  121. func (self *EthereumApi) Unregister(args string, reply *interface{}) error {
  122. self.regmut.Lock()
  123. defer self.regmut.Unlock()
  124. delete(self.register, args)
  125. return nil
  126. }
  127. func (self *EthereumApi) WatchTx(args string, reply *interface{}) error {
  128. self.regmut.Lock()
  129. defer self.regmut.Unlock()
  130. txs := self.register[args]
  131. self.register[args] = nil
  132. *reply = txs
  133. return nil
  134. }
  135. func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error {
  136. var id int
  137. filter := core.NewFilter(self.xeth().Backend())
  138. filter.SetOptions(toFilterOptions(args))
  139. filter.LogsCallback = func(logs state.Logs) {
  140. self.logMut.Lock()
  141. defer self.logMut.Unlock()
  142. self.logs[id].add(logs...)
  143. }
  144. id = self.filterManager.InstallFilter(filter)
  145. self.logs[id] = &logFilter{timeout: time.Now()}
  146. *reply = id
  147. return nil
  148. }
  149. func (self *EthereumApi) UninstallFilter(id int, reply *interface{}) error {
  150. delete(self.logs, id)
  151. self.filterManager.UninstallFilter(id)
  152. *reply = true
  153. return nil
  154. }
  155. func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error {
  156. var id int
  157. filter := core.NewFilter(self.xeth().Backend())
  158. callback := func(block *types.Block) {
  159. self.logMut.Lock()
  160. defer self.logMut.Unlock()
  161. self.logs[id].add(&state.StateLog{})
  162. }
  163. if args == "pending" {
  164. filter.PendingCallback = callback
  165. } else if args == "chain" {
  166. filter.BlockCallback = callback
  167. }
  168. id = self.filterManager.InstallFilter(filter)
  169. self.logs[id] = &logFilter{timeout: time.Now()}
  170. *reply = id
  171. return nil
  172. }
  173. func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
  174. self.logMut.Lock()
  175. defer self.logMut.Unlock()
  176. if self.logs[id] != nil {
  177. *reply = toLogs(self.logs[id].get())
  178. }
  179. return nil
  180. }
  181. func (self *EthereumApi) Logs(id int, reply *interface{}) error {
  182. self.logMut.Lock()
  183. defer self.logMut.Unlock()
  184. filter := self.filterManager.GetFilter(id)
  185. if filter != nil {
  186. *reply = toLogs(filter.Find())
  187. }
  188. return nil
  189. }
  190. func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error {
  191. filter := core.NewFilter(self.xeth().Backend())
  192. filter.SetOptions(toFilterOptions(args))
  193. *reply = toLogs(filter.Find())
  194. return nil
  195. }
  196. func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error {
  197. // This seems a bit precarious Maybe worth splitting to discrete functions
  198. if len(args.Hash) > 0 {
  199. *reply = p.xeth().BlockByHash(args.Hash)
  200. } else {
  201. *reply = p.xeth().BlockByNumber(args.BlockNumber)
  202. }
  203. return nil
  204. }
  205. func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
  206. if len(args.Gas) == 0 {
  207. args.Gas = defaultGas.String()
  208. }
  209. if len(args.GasPrice) == 0 {
  210. args.GasPrice = defaultGasPrice.String()
  211. }
  212. // TODO if no_private_key then
  213. //if _, exists := p.register[args.From]; exists {
  214. // p.register[args.From] = append(p.register[args.From], args)
  215. //} else {
  216. /*
  217. account := accounts.Get(fromHex(args.From))
  218. if account != nil {
  219. if account.Unlocked() {
  220. if !unlockAccount(account) {
  221. return
  222. }
  223. }
  224. result, _ := account.Transact(fromHex(args.To), fromHex(args.Value), fromHex(args.Gas), fromHex(args.GasPrice), fromHex(args.Data))
  225. if len(result) > 0 {
  226. *reply = toHex(result)
  227. }
  228. } else if _, exists := p.register[args.From]; exists {
  229. p.register[ags.From] = append(p.register[args.From], args)
  230. }
  231. */
  232. result, _ := p.xeth().Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
  233. *reply = result
  234. //}
  235. return nil
  236. }
  237. func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error {
  238. result, err := p.xeth().Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
  239. if err != nil {
  240. return err
  241. }
  242. *reply = result
  243. return nil
  244. }
  245. func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error {
  246. err := args.requirementsPushTx()
  247. if err != nil {
  248. return err
  249. }
  250. result, _ := p.xeth().PushTx(args.Tx)
  251. *reply = result
  252. return nil
  253. }
  254. func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error {
  255. err := args.requirements()
  256. if err != nil {
  257. return err
  258. }
  259. state := p.xeth().State().SafeGet(args.Address)
  260. value := state.StorageString(args.Key)
  261. var hx string
  262. if strings.Index(args.Key, "0x") == 0 {
  263. hx = string([]byte(args.Key)[2:])
  264. } else {
  265. // Convert the incoming string (which is a bigint) into hex
  266. i, _ := new(big.Int).SetString(args.Key, 10)
  267. hx = ethutil.Bytes2Hex(i.Bytes())
  268. }
  269. rpclogger.Debugf("GetStateAt(%s, %s)\n", args.Address, hx)
  270. *reply = map[string]string{args.Key: value.Str()}
  271. return nil
  272. }
  273. func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error {
  274. err := args.requirements()
  275. if err != nil {
  276. return err
  277. }
  278. *reply = p.xeth().State().SafeGet(args.Address).Storage()
  279. return nil
  280. }
  281. func (p *EthereumApi) GetPeerCount(reply *interface{}) error {
  282. *reply = p.xeth().PeerCount()
  283. return nil
  284. }
  285. func (p *EthereumApi) GetIsListening(reply *interface{}) error {
  286. *reply = p.xeth().IsListening()
  287. return nil
  288. }
  289. func (p *EthereumApi) GetCoinbase(reply *interface{}) error {
  290. *reply = p.xeth().Coinbase()
  291. return nil
  292. }
  293. func (p *EthereumApi) Accounts(reply *interface{}) error {
  294. *reply = p.xeth().Accounts()
  295. return nil
  296. }
  297. func (p *EthereumApi) GetIsMining(reply *interface{}) error {
  298. *reply = p.xeth().IsMining()
  299. return nil
  300. }
  301. func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error {
  302. *reply = p.xeth().SetMining(shouldmine)
  303. return nil
  304. }
  305. func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error {
  306. *reply = p.defaultBlockAge
  307. return nil
  308. }
  309. func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int64, reply *interface{}) error {
  310. p.defaultBlockAge = defaultBlockAge
  311. p.setStateByBlockNumber(p.defaultBlockAge)
  312. *reply = true
  313. return nil
  314. }
  315. func (p *EthereumApi) BlockNumber(reply *interface{}) error {
  316. *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number()
  317. return nil
  318. }
  319. func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error {
  320. err := args.requirements()
  321. if err != nil {
  322. return err
  323. }
  324. *reply = p.xeth().TxCountAt(args.Address)
  325. return nil
  326. }
  327. func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) error {
  328. err := args.requirements()
  329. if err != nil {
  330. return err
  331. }
  332. state := p.xeth().State().SafeGet(args.Address)
  333. *reply = toHex(state.Balance().Bytes())
  334. return nil
  335. }
  336. func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error {
  337. err := args.requirements()
  338. if err != nil {
  339. return err
  340. }
  341. *reply = p.xeth().CodeAt(args.Address)
  342. return nil
  343. }
  344. func (p *EthereumApi) GetCompilers(reply *interface{}) error {
  345. c := []string{"serpent"}
  346. *reply = c
  347. return nil
  348. }
  349. func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error {
  350. res, err := ethutil.Compile(script, false)
  351. if err != nil {
  352. return err
  353. }
  354. *reply = res
  355. return nil
  356. }
  357. func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error {
  358. *reply = toHex(crypto.Sha3(fromHex(args.Data)))
  359. return nil
  360. }
  361. func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error {
  362. err := args.requirements()
  363. if err != nil {
  364. return err
  365. }
  366. p.db.Put([]byte(args.Database+args.Key), []byte(args.Value))
  367. *reply = true
  368. return nil
  369. }
  370. func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error {
  371. err := args.requirements()
  372. if err != nil {
  373. return err
  374. }
  375. res, _ := p.db.Get([]byte(args.Database + args.Key))
  376. *reply = string(res)
  377. return nil
  378. }
  379. func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error {
  380. *reply = p.xeth().Whisper().NewIdentity()
  381. return nil
  382. }
  383. func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error {
  384. var id int
  385. args.Fn = func(msg xeth.WhisperMessage) {
  386. p.messagesMut.Lock()
  387. defer p.messagesMut.Unlock()
  388. p.messages[id].add(msg) // = append(p.messages[id], msg)
  389. }
  390. id = p.xeth().Whisper().Watch(args)
  391. p.messages[id] = &whisperFilter{timeout: time.Now()}
  392. *reply = id
  393. return nil
  394. }
  395. func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
  396. self.messagesMut.Lock()
  397. defer self.messagesMut.Unlock()
  398. if self.messages[id] != nil {
  399. *reply = self.messages[id].get()
  400. }
  401. return nil
  402. }
  403. func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error {
  404. err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl)
  405. if err != nil {
  406. return err
  407. }
  408. *reply = true
  409. return nil
  410. }
  411. func (p *EthereumApi) HasWhisperIdentity(args string, reply *interface{}) error {
  412. *reply = p.xeth().Whisper().HasIdentity(args)
  413. return nil
  414. }
  415. func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error {
  416. *reply = p.xeth().Whisper().Messages(id)
  417. return nil
  418. }
  419. func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
  420. // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC
  421. rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
  422. switch req.Method {
  423. case "eth_coinbase":
  424. return p.GetCoinbase(reply)
  425. case "eth_listening":
  426. return p.GetIsListening(reply)
  427. case "eth_mining":
  428. return p.GetIsMining(reply)
  429. case "eth_setMining":
  430. args, err := req.ToBoolArgs()
  431. if err != nil {
  432. return err
  433. }
  434. return p.SetMining(args, reply)
  435. case "eth_defaultBlock":
  436. return p.GetDefaultBlockAge(reply)
  437. case "eth_setDefaultBlock":
  438. args, err := req.ToIntArgs()
  439. if err != nil {
  440. return err
  441. }
  442. return p.SetDefaultBlockAge(int64(args), reply)
  443. case "eth_peerCount":
  444. return p.GetPeerCount(reply)
  445. case "eth_number":
  446. return p.BlockNumber(reply)
  447. case "eth_accounts":
  448. return p.Accounts(reply)
  449. case "eth_countAt":
  450. args, err := req.ToGetTxCountArgs()
  451. if err != nil {
  452. return err
  453. }
  454. return p.GetTxCountAt(args, reply)
  455. case "eth_codeAt":
  456. args, err := req.ToGetCodeAtArgs()
  457. if err != nil {
  458. return err
  459. }
  460. return p.GetCodeAt(args, reply)
  461. case "eth_balanceAt":
  462. args, err := req.ToGetBalanceArgs()
  463. if err != nil {
  464. return err
  465. }
  466. return p.GetBalanceAt(args, reply)
  467. case "eth_stateAt":
  468. args, err := req.ToGetStateArgs()
  469. if err != nil {
  470. return err
  471. }
  472. return p.GetStateAt(args, reply)
  473. case "eth_storageAt":
  474. args, err := req.ToStorageAtArgs()
  475. if err != nil {
  476. return err
  477. }
  478. return p.GetStorageAt(args, reply)
  479. case "eth_blockByNumber", "eth_blockByHash":
  480. args, err := req.ToGetBlockArgs()
  481. if err != nil {
  482. return err
  483. }
  484. return p.GetBlock(args, reply)
  485. case "eth_transact":
  486. args, err := req.ToNewTxArgs()
  487. if err != nil {
  488. return err
  489. }
  490. return p.Transact(args, reply)
  491. case "eth_call":
  492. args, err := req.ToNewTxArgs()
  493. if err != nil {
  494. return err
  495. }
  496. return p.Call(args, reply)
  497. case "eth_newFilter":
  498. args, err := req.ToFilterArgs()
  499. if err != nil {
  500. return err
  501. }
  502. return p.NewFilter(args, reply)
  503. case "eth_newFilterString":
  504. args, err := req.ToFilterStringArgs()
  505. if err != nil {
  506. return err
  507. }
  508. return p.NewFilterString(args, reply)
  509. case "eth_uninstallFilter":
  510. args, err := req.ToUninstallFilterArgs()
  511. if err != nil {
  512. return err
  513. }
  514. return p.UninstallFilter(args, reply)
  515. case "eth_changed":
  516. args, err := req.ToIdArgs()
  517. if err != nil {
  518. return err
  519. }
  520. return p.FilterChanged(args, reply)
  521. case "eth_filterLogs":
  522. args, err := req.ToIdArgs()
  523. if err != nil {
  524. return err
  525. }
  526. return p.Logs(args, reply)
  527. case "eth_logs":
  528. args, err := req.ToFilterArgs()
  529. if err != nil {
  530. return err
  531. }
  532. return p.AllLogs(args, reply)
  533. case "eth_gasPrice":
  534. *reply = toHex(defaultGasPrice.Bytes())
  535. return nil
  536. case "eth_register":
  537. args, err := req.ToRegisterArgs()
  538. if err != nil {
  539. return err
  540. }
  541. return p.Register(args, reply)
  542. case "eth_unregister":
  543. args, err := req.ToRegisterArgs()
  544. if err != nil {
  545. return err
  546. }
  547. return p.Unregister(args, reply)
  548. case "eth_watchTx":
  549. args, err := req.ToWatchTxArgs()
  550. if err != nil {
  551. return err
  552. }
  553. return p.WatchTx(args, reply)
  554. case "eth_compilers":
  555. return p.GetCompilers(reply)
  556. case "eth_serpent":
  557. args, err := req.ToCompileArgs()
  558. if err != nil {
  559. return err
  560. }
  561. return p.CompileSerpent(args, reply)
  562. case "web3_sha3":
  563. args, err := req.ToSha3Args()
  564. if err != nil {
  565. return err
  566. }
  567. return p.Sha3(args, reply)
  568. case "db_put":
  569. args, err := req.ToDbPutArgs()
  570. if err != nil {
  571. return err
  572. }
  573. return p.DbPut(args, reply)
  574. case "db_get":
  575. args, err := req.ToDbGetArgs()
  576. if err != nil {
  577. return err
  578. }
  579. return p.DbGet(args, reply)
  580. case "shh_newIdentity":
  581. return p.NewWhisperIdentity(reply)
  582. case "shh_newFilter":
  583. args, err := req.ToWhisperFilterArgs()
  584. if err != nil {
  585. return err
  586. }
  587. return p.NewWhisperFilter(args, reply)
  588. case "shh_changed":
  589. args, err := req.ToIdArgs()
  590. if err != nil {
  591. return err
  592. }
  593. return p.MessagesChanged(args, reply)
  594. case "shh_post":
  595. args, err := req.ToWhisperPostArgs()
  596. if err != nil {
  597. return err
  598. }
  599. return p.WhisperPost(args, reply)
  600. case "shh_haveIdentity":
  601. args, err := req.ToWhisperHasIdentityArgs()
  602. if err != nil {
  603. return err
  604. }
  605. return p.HasWhisperIdentity(args, reply)
  606. case "shh_getMessages":
  607. args, err := req.ToIdArgs()
  608. if err != nil {
  609. return err
  610. }
  611. return p.WhisperMessages(args, reply)
  612. default:
  613. return NewErrorWithMessage(errNotImplemented, req.Method)
  614. }
  615. rpclogger.DebugDetailf("Reply: %T %s", reply, reply)
  616. return nil
  617. }
  618. func (self *EthereumApi) xeth() *xeth.XEth {
  619. self.xethMu.RLock()
  620. defer self.xethMu.RUnlock()
  621. return self.eth
  622. }
  623. func (self *EthereumApi) useState(statedb *state.StateDB) {
  624. self.xethMu.Lock()
  625. defer self.xethMu.Unlock()
  626. self.eth = self.eth.UseState(statedb)
  627. }
  628. func t(f ui.Frontend) {
  629. // Call the password dialog
  630. ret, err := f.Call("PasswordDialog")
  631. if err != nil {
  632. fmt.Println(err)
  633. }
  634. // Get the first argument
  635. t, _ := ret.Get(0)
  636. fmt.Println("return:", t)
  637. }