api.go 17 KB

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