You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
19 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
9 years ago
7 years ago
7 years ago
9 years ago
9 years ago
7 years ago
7 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strings"
  10. "github.com/spf13/cobra"
  11. cmn "github.com/tendermint/tendermint/libs/common"
  12. "github.com/tendermint/tendermint/libs/log"
  13. abcicli "github.com/tendermint/tendermint/abci/client"
  14. "github.com/tendermint/tendermint/abci/example/code"
  15. "github.com/tendermint/tendermint/abci/example/counter"
  16. "github.com/tendermint/tendermint/abci/example/kvstore"
  17. "github.com/tendermint/tendermint/abci/server"
  18. servertest "github.com/tendermint/tendermint/abci/tests/server"
  19. "github.com/tendermint/tendermint/abci/types"
  20. "github.com/tendermint/tendermint/abci/version"
  21. "github.com/tendermint/tendermint/crypto/merkle"
  22. )
  23. // client is a global variable so it can be reused by the console
  24. var (
  25. client abcicli.Client
  26. logger log.Logger
  27. )
  28. // flags
  29. var (
  30. // global
  31. flagAddress string
  32. flagAbci string
  33. flagVerbose bool // for the println output
  34. flagLogLevel string // for the logger
  35. // query
  36. flagPath string
  37. flagHeight int
  38. flagProve bool
  39. // counter
  40. flagSerial bool
  41. // kvstore
  42. flagPersist string
  43. )
  44. var RootCmd = &cobra.Command{
  45. Use: "abci-cli",
  46. Short: "the ABCI CLI tool wraps an ABCI client",
  47. Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers",
  48. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  49. switch cmd.Use {
  50. case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run
  51. return nil
  52. case "version": // skip running for version command
  53. return nil
  54. }
  55. if logger == nil {
  56. allowLevel, err := log.AllowLevel(flagLogLevel)
  57. if err != nil {
  58. return err
  59. }
  60. logger = log.NewFilter(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), allowLevel)
  61. }
  62. if client == nil {
  63. var err error
  64. client, err = abcicli.NewClient(flagAddress, flagAbci, false)
  65. if err != nil {
  66. return err
  67. }
  68. client.SetLogger(logger.With("module", "abci-client"))
  69. if err := client.Start(); err != nil {
  70. return err
  71. }
  72. }
  73. return nil
  74. },
  75. }
  76. // Structure for data passed to print response.
  77. type response struct {
  78. // generic abci response
  79. Data []byte
  80. Code uint32
  81. Info string
  82. Log string
  83. Query *queryResponse
  84. }
  85. type queryResponse struct {
  86. Key []byte
  87. Value []byte
  88. Height int64
  89. Proof *merkle.Proof
  90. }
  91. func Execute() error {
  92. addGlobalFlags()
  93. addCommands()
  94. return RootCmd.Execute()
  95. }
  96. func addGlobalFlags() {
  97. RootCmd.PersistentFlags().StringVarP(&flagAddress, "address", "", "tcp://0.0.0.0:26658", "address of application socket")
  98. RootCmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc")
  99. RootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "print the command and results as if it were a console session")
  100. RootCmd.PersistentFlags().StringVarP(&flagLogLevel, "log_level", "", "debug", "set the logger level")
  101. }
  102. func addQueryFlags() {
  103. queryCmd.PersistentFlags().StringVarP(&flagPath, "path", "", "/store", "path to prefix query with")
  104. queryCmd.PersistentFlags().IntVarP(&flagHeight, "height", "", 0, "height to query the blockchain at")
  105. queryCmd.PersistentFlags().BoolVarP(&flagProve, "prove", "", false, "whether or not to return a merkle proof of the query result")
  106. }
  107. func addCounterFlags() {
  108. counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions")
  109. }
  110. func addDummyFlags() {
  111. dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
  112. }
  113. func addKVStoreFlags() {
  114. kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
  115. }
  116. func addCommands() {
  117. RootCmd.AddCommand(batchCmd)
  118. RootCmd.AddCommand(consoleCmd)
  119. RootCmd.AddCommand(echoCmd)
  120. RootCmd.AddCommand(infoCmd)
  121. RootCmd.AddCommand(setOptionCmd)
  122. RootCmd.AddCommand(deliverTxCmd)
  123. RootCmd.AddCommand(checkTxCmd)
  124. RootCmd.AddCommand(commitCmd)
  125. RootCmd.AddCommand(versionCmd)
  126. RootCmd.AddCommand(testCmd)
  127. addQueryFlags()
  128. RootCmd.AddCommand(queryCmd)
  129. // examples
  130. addCounterFlags()
  131. RootCmd.AddCommand(counterCmd)
  132. // deprecated, left for backwards compatibility
  133. addDummyFlags()
  134. RootCmd.AddCommand(dummyCmd)
  135. // replaces dummy, see issue #196
  136. addKVStoreFlags()
  137. RootCmd.AddCommand(kvstoreCmd)
  138. }
  139. var batchCmd = &cobra.Command{
  140. Use: "batch",
  141. Short: "run a batch of abci commands against an application",
  142. Long: `run a batch of abci commands against an application
  143. This command is run by piping in a file containing a series of commands
  144. you'd like to run:
  145. abci-cli batch < example.file
  146. where example.file looks something like:
  147. set_option serial on
  148. check_tx 0x00
  149. check_tx 0xff
  150. deliver_tx 0x00
  151. check_tx 0x00
  152. deliver_tx 0x01
  153. deliver_tx 0x04
  154. info
  155. `,
  156. Args: cobra.ExactArgs(0),
  157. RunE: func(cmd *cobra.Command, args []string) error {
  158. return cmdBatch(cmd, args)
  159. },
  160. }
  161. var consoleCmd = &cobra.Command{
  162. Use: "console",
  163. Short: "start an interactive ABCI console for multiple commands",
  164. Long: `start an interactive ABCI console for multiple commands
  165. This command opens an interactive console for running any of the other commands
  166. without opening a new connection each time
  167. `,
  168. Args: cobra.ExactArgs(0),
  169. ValidArgs: []string{"echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"},
  170. RunE: func(cmd *cobra.Command, args []string) error {
  171. return cmdConsole(cmd, args)
  172. },
  173. }
  174. var echoCmd = &cobra.Command{
  175. Use: "echo",
  176. Short: "have the application echo a message",
  177. Long: "have the application echo a message",
  178. Args: cobra.ExactArgs(1),
  179. RunE: func(cmd *cobra.Command, args []string) error {
  180. return cmdEcho(cmd, args)
  181. },
  182. }
  183. var infoCmd = &cobra.Command{
  184. Use: "info",
  185. Short: "get some info about the application",
  186. Long: "get some info about the application",
  187. Args: cobra.ExactArgs(0),
  188. RunE: func(cmd *cobra.Command, args []string) error {
  189. return cmdInfo(cmd, args)
  190. },
  191. }
  192. var setOptionCmd = &cobra.Command{
  193. Use: "set_option",
  194. Short: "set an option on the application",
  195. Long: "set an option on the application",
  196. Args: cobra.ExactArgs(2),
  197. RunE: func(cmd *cobra.Command, args []string) error {
  198. return cmdSetOption(cmd, args)
  199. },
  200. }
  201. var deliverTxCmd = &cobra.Command{
  202. Use: "deliver_tx",
  203. Short: "deliver a new transaction to the application",
  204. Long: "deliver a new transaction to the application",
  205. Args: cobra.ExactArgs(1),
  206. RunE: func(cmd *cobra.Command, args []string) error {
  207. return cmdDeliverTx(cmd, args)
  208. },
  209. }
  210. var checkTxCmd = &cobra.Command{
  211. Use: "check_tx",
  212. Short: "validate a transaction",
  213. Long: "validate a transaction",
  214. Args: cobra.ExactArgs(1),
  215. RunE: func(cmd *cobra.Command, args []string) error {
  216. return cmdCheckTx(cmd, args)
  217. },
  218. }
  219. var commitCmd = &cobra.Command{
  220. Use: "commit",
  221. Short: "commit the application state and return the Merkle root hash",
  222. Long: "commit the application state and return the Merkle root hash",
  223. Args: cobra.ExactArgs(0),
  224. RunE: func(cmd *cobra.Command, args []string) error {
  225. return cmdCommit(cmd, args)
  226. },
  227. }
  228. var versionCmd = &cobra.Command{
  229. Use: "version",
  230. Short: "print ABCI console version",
  231. Long: "print ABCI console version",
  232. Args: cobra.ExactArgs(0),
  233. RunE: func(cmd *cobra.Command, args []string) error {
  234. fmt.Println(version.Version)
  235. return nil
  236. },
  237. }
  238. var queryCmd = &cobra.Command{
  239. Use: "query",
  240. Short: "query the application state",
  241. Long: "query the application state",
  242. Args: cobra.ExactArgs(1),
  243. RunE: func(cmd *cobra.Command, args []string) error {
  244. return cmdQuery(cmd, args)
  245. },
  246. }
  247. var counterCmd = &cobra.Command{
  248. Use: "counter",
  249. Short: "ABCI demo example",
  250. Long: "ABCI demo example",
  251. Args: cobra.ExactArgs(0),
  252. RunE: func(cmd *cobra.Command, args []string) error {
  253. return cmdCounter(cmd, args)
  254. },
  255. }
  256. // deprecated, left for backwards compatibility
  257. var dummyCmd = &cobra.Command{
  258. Use: "dummy",
  259. Deprecated: "use: [abci-cli kvstore] instead",
  260. Short: "ABCI demo example",
  261. Long: "ABCI demo example",
  262. Args: cobra.ExactArgs(0),
  263. RunE: func(cmd *cobra.Command, args []string) error {
  264. return cmdKVStore(cmd, args)
  265. },
  266. }
  267. var kvstoreCmd = &cobra.Command{
  268. Use: "kvstore",
  269. Short: "ABCI demo example",
  270. Long: "ABCI demo example",
  271. Args: cobra.ExactArgs(0),
  272. RunE: func(cmd *cobra.Command, args []string) error {
  273. return cmdKVStore(cmd, args)
  274. },
  275. }
  276. var testCmd = &cobra.Command{
  277. Use: "test",
  278. Short: "run integration tests",
  279. Long: "run integration tests",
  280. Args: cobra.ExactArgs(0),
  281. RunE: func(cmd *cobra.Command, args []string) error {
  282. return cmdTest(cmd, args)
  283. },
  284. }
  285. // Generates new Args array based off of previous call args to maintain flag persistence
  286. func persistentArgs(line []byte) []string {
  287. // generate the arguments to run from original os.Args
  288. // to maintain flag arguments
  289. args := os.Args
  290. args = args[:len(args)-1] // remove the previous command argument
  291. if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors
  292. args = append(args, strings.Split(string(line), " ")...)
  293. }
  294. return args
  295. }
  296. //--------------------------------------------------------------------------------
  297. func compose(fs []func() error) error {
  298. if len(fs) == 0 {
  299. return nil
  300. } else {
  301. err := fs[0]()
  302. if err == nil {
  303. return compose(fs[1:])
  304. } else {
  305. return err
  306. }
  307. }
  308. }
  309. func cmdTest(cmd *cobra.Command, args []string) error {
  310. return compose(
  311. []func() error{
  312. func() error { return servertest.InitChain(client) },
  313. func() error { return servertest.SetOption(client, "serial", "on") },
  314. func() error { return servertest.Commit(client, nil) },
  315. func() error { return servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) },
  316. func() error { return servertest.Commit(client, nil) },
  317. func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil) },
  318. func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) },
  319. func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) },
  320. func() error { return servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil) },
  321. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) },
  322. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) },
  323. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) },
  324. func() error {
  325. return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil)
  326. },
  327. func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) },
  328. })
  329. }
  330. func cmdBatch(cmd *cobra.Command, args []string) error {
  331. bufReader := bufio.NewReader(os.Stdin)
  332. for {
  333. line, more, err := bufReader.ReadLine()
  334. if more {
  335. return errors.New("Input line is too long")
  336. } else if err == io.EOF {
  337. break
  338. } else if len(line) == 0 {
  339. continue
  340. } else if err != nil {
  341. return err
  342. }
  343. cmdArgs := persistentArgs(line)
  344. if err := muxOnCommands(cmd, cmdArgs); err != nil {
  345. return err
  346. }
  347. fmt.Println()
  348. }
  349. return nil
  350. }
  351. func cmdConsole(cmd *cobra.Command, args []string) error {
  352. for {
  353. fmt.Printf("> ")
  354. bufReader := bufio.NewReader(os.Stdin)
  355. line, more, err := bufReader.ReadLine()
  356. if more {
  357. return errors.New("Input is too long")
  358. } else if err != nil {
  359. return err
  360. }
  361. pArgs := persistentArgs(line)
  362. if err := muxOnCommands(cmd, pArgs); err != nil {
  363. return err
  364. }
  365. }
  366. return nil
  367. }
  368. func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
  369. if len(pArgs) < 2 {
  370. return errors.New("expecting persistent args of the form: abci-cli [command] <...>")
  371. }
  372. // TODO: this parsing is fragile
  373. args := []string{}
  374. for i := 0; i < len(pArgs); i++ {
  375. arg := pArgs[i]
  376. // check for flags
  377. if strings.HasPrefix(arg, "-") {
  378. // if it has an equal, we can just skip
  379. if strings.Contains(arg, "=") {
  380. continue
  381. }
  382. // if its a boolean, we can just skip
  383. _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-"))
  384. if err == nil {
  385. continue
  386. }
  387. // otherwise, we need to skip the next one too
  388. i += 1
  389. continue
  390. }
  391. // append the actual arg
  392. args = append(args, arg)
  393. }
  394. var subCommand string
  395. var actualArgs []string
  396. if len(args) > 1 {
  397. subCommand = args[1]
  398. }
  399. if len(args) > 2 {
  400. actualArgs = args[2:]
  401. }
  402. cmd.Use = subCommand // for later print statements ...
  403. switch strings.ToLower(subCommand) {
  404. case "check_tx":
  405. return cmdCheckTx(cmd, actualArgs)
  406. case "commit":
  407. return cmdCommit(cmd, actualArgs)
  408. case "deliver_tx":
  409. return cmdDeliverTx(cmd, actualArgs)
  410. case "echo":
  411. return cmdEcho(cmd, actualArgs)
  412. case "info":
  413. return cmdInfo(cmd, actualArgs)
  414. case "query":
  415. return cmdQuery(cmd, actualArgs)
  416. case "set_option":
  417. return cmdSetOption(cmd, actualArgs)
  418. default:
  419. return cmdUnimplemented(cmd, pArgs)
  420. }
  421. }
  422. func cmdUnimplemented(cmd *cobra.Command, args []string) error {
  423. msg := "unimplemented command"
  424. if len(args) > 0 {
  425. msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
  426. }
  427. printResponse(cmd, args, response{
  428. Code: codeBad,
  429. Log: msg,
  430. })
  431. fmt.Println("Available commands:")
  432. fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
  433. fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
  434. fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
  435. fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
  436. fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
  437. fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
  438. fmt.Printf("%s: %s\n", setOptionCmd.Use, setOptionCmd.Short)
  439. fmt.Println("Use \"[command] --help\" for more information about a command.")
  440. return nil
  441. }
  442. // Have the application echo a message
  443. func cmdEcho(cmd *cobra.Command, args []string) error {
  444. msg := ""
  445. if len(args) > 0 {
  446. msg = args[0]
  447. }
  448. res, err := client.EchoSync(msg)
  449. if err != nil {
  450. return err
  451. }
  452. printResponse(cmd, args, response{
  453. Data: []byte(res.Message),
  454. })
  455. return nil
  456. }
  457. // Get some info from the application
  458. func cmdInfo(cmd *cobra.Command, args []string) error {
  459. var version string
  460. if len(args) == 1 {
  461. version = args[0]
  462. }
  463. res, err := client.InfoSync(types.RequestInfo{Version: version})
  464. if err != nil {
  465. return err
  466. }
  467. printResponse(cmd, args, response{
  468. Data: []byte(res.Data),
  469. })
  470. return nil
  471. }
  472. const codeBad uint32 = 10
  473. // Set an option on the application
  474. func cmdSetOption(cmd *cobra.Command, args []string) error {
  475. if len(args) < 2 {
  476. printResponse(cmd, args, response{
  477. Code: codeBad,
  478. Log: "want at least arguments of the form: <key> <value>",
  479. })
  480. return nil
  481. }
  482. key, val := args[0], args[1]
  483. _, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: val})
  484. if err != nil {
  485. return err
  486. }
  487. printResponse(cmd, args, response{Log: "OK (SetOption doesn't return anything.)"}) // NOTE: Nothing to show...
  488. return nil
  489. }
  490. // Append a new tx to application
  491. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  492. if len(args) == 0 {
  493. printResponse(cmd, args, response{
  494. Code: codeBad,
  495. Log: "want the tx",
  496. })
  497. return nil
  498. }
  499. txBytes, err := stringOrHexToBytes(args[0])
  500. if err != nil {
  501. return err
  502. }
  503. res, err := client.DeliverTxSync(txBytes)
  504. if err != nil {
  505. return err
  506. }
  507. printResponse(cmd, args, response{
  508. Code: res.Code,
  509. Data: res.Data,
  510. Info: res.Info,
  511. Log: res.Log,
  512. })
  513. return nil
  514. }
  515. // Validate a tx
  516. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  517. if len(args) == 0 {
  518. printResponse(cmd, args, response{
  519. Code: codeBad,
  520. Info: "want the tx",
  521. })
  522. return nil
  523. }
  524. txBytes, err := stringOrHexToBytes(args[0])
  525. if err != nil {
  526. return err
  527. }
  528. res, err := client.CheckTxSync(txBytes)
  529. if err != nil {
  530. return err
  531. }
  532. printResponse(cmd, args, response{
  533. Code: res.Code,
  534. Data: res.Data,
  535. Info: res.Info,
  536. Log: res.Log,
  537. })
  538. return nil
  539. }
  540. // Get application Merkle root hash
  541. func cmdCommit(cmd *cobra.Command, args []string) error {
  542. res, err := client.CommitSync()
  543. if err != nil {
  544. return err
  545. }
  546. printResponse(cmd, args, response{
  547. Data: res.Data,
  548. })
  549. return nil
  550. }
  551. // Query application state
  552. func cmdQuery(cmd *cobra.Command, args []string) error {
  553. if len(args) == 0 {
  554. printResponse(cmd, args, response{
  555. Code: codeBad,
  556. Info: "want the query",
  557. Log: "",
  558. })
  559. return nil
  560. }
  561. queryBytes, err := stringOrHexToBytes(args[0])
  562. if err != nil {
  563. return err
  564. }
  565. resQuery, err := client.QuerySync(types.RequestQuery{
  566. Data: queryBytes,
  567. Path: flagPath,
  568. Height: int64(flagHeight),
  569. Prove: flagProve,
  570. })
  571. if err != nil {
  572. return err
  573. }
  574. printResponse(cmd, args, response{
  575. Code: resQuery.Code,
  576. Info: resQuery.Info,
  577. Log: resQuery.Log,
  578. Query: &queryResponse{
  579. Key: resQuery.Key,
  580. Value: resQuery.Value,
  581. Height: resQuery.Height,
  582. Proof: resQuery.Proof,
  583. },
  584. })
  585. return nil
  586. }
  587. func cmdCounter(cmd *cobra.Command, args []string) error {
  588. app := counter.NewCounterApplication(flagSerial)
  589. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  590. // Start the listener
  591. srv, err := server.NewServer(flagAddress, flagAbci, app)
  592. if err != nil {
  593. return err
  594. }
  595. srv.SetLogger(logger.With("module", "abci-server"))
  596. if err := srv.Start(); err != nil {
  597. return err
  598. }
  599. // Wait forever
  600. cmn.TrapSignal(func() {
  601. // Cleanup
  602. srv.Stop()
  603. })
  604. return nil
  605. }
  606. func cmdKVStore(cmd *cobra.Command, args []string) error {
  607. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  608. // Create the application - in memory or persisted to disk
  609. var app types.Application
  610. if flagPersist == "" {
  611. app = kvstore.NewKVStoreApplication()
  612. } else {
  613. app = kvstore.NewPersistentKVStoreApplication(flagPersist)
  614. app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
  615. }
  616. // Start the listener
  617. srv, err := server.NewServer(flagAddress, flagAbci, app)
  618. if err != nil {
  619. return err
  620. }
  621. srv.SetLogger(logger.With("module", "abci-server"))
  622. if err := srv.Start(); err != nil {
  623. return err
  624. }
  625. // Wait forever
  626. cmn.TrapSignal(func() {
  627. // Cleanup
  628. srv.Stop()
  629. })
  630. return nil
  631. }
  632. //--------------------------------------------------------------------------------
  633. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  634. if flagVerbose {
  635. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  636. }
  637. // Always print the status code.
  638. if rsp.Code == types.CodeTypeOK {
  639. fmt.Printf("-> code: OK\n")
  640. } else {
  641. fmt.Printf("-> code: %d\n", rsp.Code)
  642. }
  643. if len(rsp.Data) != 0 {
  644. // Do no print this line when using the commit command
  645. // because the string comes out as gibberish
  646. if cmd.Use != "commit" {
  647. fmt.Printf("-> data: %s\n", rsp.Data)
  648. }
  649. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  650. }
  651. if rsp.Log != "" {
  652. fmt.Printf("-> log: %s\n", rsp.Log)
  653. }
  654. if rsp.Query != nil {
  655. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  656. if rsp.Query.Key != nil {
  657. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  658. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  659. }
  660. if rsp.Query.Value != nil {
  661. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  662. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  663. }
  664. if rsp.Query.Proof != nil {
  665. fmt.Printf("-> proof: %#v\n", rsp.Query.Proof)
  666. }
  667. }
  668. }
  669. // NOTE: s is interpreted as a string unless prefixed with 0x
  670. func stringOrHexToBytes(s string) ([]byte, error) {
  671. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  672. b, err := hex.DecodeString(s[2:])
  673. if err != nil {
  674. err = fmt.Errorf("Error decoding hex argument: %s", err.Error())
  675. return nil, err
  676. }
  677. return b, nil
  678. }
  679. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  680. err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  681. return nil, err
  682. }
  683. return []byte(s[1 : len(s)-1]), nil
  684. }