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.

748 lines
18 KiB

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