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.

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