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.

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