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.

754 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. return nil
  349. }
  350. func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
  351. if len(pArgs) < 2 {
  352. return errors.New("expecting persistent args of the form: abci-cli [command] <...>")
  353. }
  354. // TODO: this parsing is fragile
  355. args := []string{}
  356. for i := 0; i < len(pArgs); i++ {
  357. arg := pArgs[i]
  358. // check for flags
  359. if strings.HasPrefix(arg, "-") {
  360. // if it has an equal, we can just skip
  361. if strings.Contains(arg, "=") {
  362. continue
  363. }
  364. // if its a boolean, we can just skip
  365. _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-"))
  366. if err == nil {
  367. continue
  368. }
  369. // otherwise, we need to skip the next one too
  370. i += 1
  371. continue
  372. }
  373. // append the actual arg
  374. args = append(args, arg)
  375. }
  376. var subCommand string
  377. var actualArgs []string
  378. if len(args) > 1 {
  379. subCommand = args[1]
  380. }
  381. if len(args) > 2 {
  382. actualArgs = args[2:]
  383. }
  384. cmd.Use = subCommand // for later print statements ...
  385. switch strings.ToLower(subCommand) {
  386. case "check_tx":
  387. return cmdCheckTx(cmd, actualArgs)
  388. case "commit":
  389. return cmdCommit(cmd, actualArgs)
  390. case "deliver_tx":
  391. return cmdDeliverTx(cmd, actualArgs)
  392. case "echo":
  393. return cmdEcho(cmd, actualArgs)
  394. case "info":
  395. return cmdInfo(cmd, actualArgs)
  396. case "query":
  397. return cmdQuery(cmd, actualArgs)
  398. case "set_option":
  399. return cmdSetOption(cmd, actualArgs)
  400. default:
  401. return cmdUnimplemented(cmd, pArgs)
  402. }
  403. }
  404. func cmdUnimplemented(cmd *cobra.Command, args []string) error {
  405. msg := "unimplemented command"
  406. if len(args) > 0 {
  407. msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
  408. }
  409. printResponse(cmd, args, response{
  410. Code: codeBad,
  411. Log: msg,
  412. })
  413. fmt.Println("Available commands:")
  414. fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
  415. fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
  416. fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
  417. fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
  418. fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
  419. fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
  420. fmt.Printf("%s: %s\n", setOptionCmd.Use, setOptionCmd.Short)
  421. fmt.Println("Use \"[command] --help\" for more information about a command.")
  422. return nil
  423. }
  424. // Have the application echo a message
  425. func cmdEcho(cmd *cobra.Command, args []string) error {
  426. msg := ""
  427. if len(args) > 0 {
  428. msg = args[0]
  429. }
  430. res, err := client.EchoSync(msg)
  431. if err != nil {
  432. return err
  433. }
  434. printResponse(cmd, args, response{
  435. Data: []byte(res.Message),
  436. })
  437. return nil
  438. }
  439. // Get some info from the application
  440. func cmdInfo(cmd *cobra.Command, args []string) error {
  441. var version string
  442. if len(args) == 1 {
  443. version = args[0]
  444. }
  445. res, err := client.InfoSync(types.RequestInfo{Version: version})
  446. if err != nil {
  447. return err
  448. }
  449. printResponse(cmd, args, response{
  450. Data: []byte(res.Data),
  451. })
  452. return nil
  453. }
  454. const codeBad uint32 = 10
  455. // Set an option on the application
  456. func cmdSetOption(cmd *cobra.Command, args []string) error {
  457. if len(args) < 2 {
  458. printResponse(cmd, args, response{
  459. Code: codeBad,
  460. Log: "want at least arguments of the form: <key> <value>",
  461. })
  462. return nil
  463. }
  464. key, val := args[0], args[1]
  465. _, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: val})
  466. if err != nil {
  467. return err
  468. }
  469. printResponse(cmd, args, response{Log: "OK (SetOption doesn't return anything.)"}) // NOTE: Nothing to show...
  470. return nil
  471. }
  472. // Append a new tx to application
  473. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  474. if len(args) == 0 {
  475. printResponse(cmd, args, response{
  476. Code: codeBad,
  477. Log: "want the tx",
  478. })
  479. return nil
  480. }
  481. txBytes, err := stringOrHexToBytes(args[0])
  482. if err != nil {
  483. return err
  484. }
  485. res, err := client.DeliverTxSync(txBytes)
  486. if err != nil {
  487. return err
  488. }
  489. printResponse(cmd, args, response{
  490. Code: res.Code,
  491. Data: res.Data,
  492. Info: res.Info,
  493. Log: res.Log,
  494. })
  495. return nil
  496. }
  497. // Validate a tx
  498. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  499. if len(args) == 0 {
  500. printResponse(cmd, args, response{
  501. Code: codeBad,
  502. Info: "want the tx",
  503. })
  504. return nil
  505. }
  506. txBytes, err := stringOrHexToBytes(args[0])
  507. if err != nil {
  508. return err
  509. }
  510. res, err := client.CheckTxSync(txBytes)
  511. if err != nil {
  512. return err
  513. }
  514. printResponse(cmd, args, response{
  515. Code: res.Code,
  516. Data: res.Data,
  517. Info: res.Info,
  518. Log: res.Log,
  519. })
  520. return nil
  521. }
  522. // Get application Merkle root hash
  523. func cmdCommit(cmd *cobra.Command, args []string) error {
  524. res, err := client.CommitSync()
  525. if err != nil {
  526. return err
  527. }
  528. printResponse(cmd, args, response{
  529. Data: res.Data,
  530. })
  531. return nil
  532. }
  533. // Query application state
  534. func cmdQuery(cmd *cobra.Command, args []string) error {
  535. if len(args) == 0 {
  536. printResponse(cmd, args, response{
  537. Code: codeBad,
  538. Info: "want the query",
  539. Log: "",
  540. })
  541. return nil
  542. }
  543. queryBytes, err := stringOrHexToBytes(args[0])
  544. if err != nil {
  545. return err
  546. }
  547. resQuery, err := client.QuerySync(types.RequestQuery{
  548. Data: queryBytes,
  549. Path: flagPath,
  550. Height: int64(flagHeight),
  551. Prove: flagProve,
  552. })
  553. if err != nil {
  554. return err
  555. }
  556. printResponse(cmd, args, response{
  557. Code: resQuery.Code,
  558. Info: resQuery.Info,
  559. Log: resQuery.Log,
  560. Query: &queryResponse{
  561. Key: resQuery.Key,
  562. Value: resQuery.Value,
  563. Height: resQuery.Height,
  564. Proof: resQuery.Proof,
  565. },
  566. })
  567. return nil
  568. }
  569. func cmdCounter(cmd *cobra.Command, args []string) error {
  570. app := counter.NewCounterApplication(flagSerial)
  571. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  572. // Start the listener
  573. srv, err := server.NewServer(flagAddress, flagAbci, app)
  574. if err != nil {
  575. return err
  576. }
  577. srv.SetLogger(logger.With("module", "abci-server"))
  578. if err := srv.Start(); err != nil {
  579. return err
  580. }
  581. // Wait forever
  582. cmn.TrapSignal(func() {
  583. // Cleanup
  584. srv.Stop()
  585. })
  586. return nil
  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. // Wait forever
  608. cmn.TrapSignal(func() {
  609. // Cleanup
  610. srv.Stop()
  611. })
  612. return nil
  613. }
  614. //--------------------------------------------------------------------------------
  615. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  616. if flagVerbose {
  617. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  618. }
  619. // Always print the status code.
  620. if rsp.Code == types.CodeTypeOK {
  621. fmt.Printf("-> code: OK\n")
  622. } else {
  623. fmt.Printf("-> code: %d\n", rsp.Code)
  624. }
  625. if len(rsp.Data) != 0 {
  626. // Do no print this line when using the commit command
  627. // because the string comes out as gibberish
  628. if cmd.Use != "commit" {
  629. fmt.Printf("-> data: %s\n", rsp.Data)
  630. }
  631. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  632. }
  633. if rsp.Log != "" {
  634. fmt.Printf("-> log: %s\n", rsp.Log)
  635. }
  636. if rsp.Query != nil {
  637. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  638. if rsp.Query.Key != nil {
  639. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  640. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  641. }
  642. if rsp.Query.Value != nil {
  643. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  644. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  645. }
  646. if rsp.Query.Proof != nil {
  647. fmt.Printf("-> proof: %#v\n", rsp.Query.Proof)
  648. }
  649. }
  650. }
  651. // NOTE: s is interpreted as a string unless prefixed with 0x
  652. func stringOrHexToBytes(s string) ([]byte, error) {
  653. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  654. b, err := hex.DecodeString(s[2:])
  655. if err != nil {
  656. err = fmt.Errorf("Error decoding hex argument: %s", err.Error())
  657. return nil, err
  658. }
  659. return b, nil
  660. }
  661. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  662. err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  663. return nil, err
  664. }
  665. return []byte(s[1 : len(s)-1]), nil
  666. }