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.

770 lines
19 KiB

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