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.

667 lines
16 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
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
7 years ago
7 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
9 years ago
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "os/signal"
  10. "strings"
  11. "syscall"
  12. "github.com/spf13/cobra"
  13. "github.com/tendermint/tendermint/libs/log"
  14. abciclient "github.com/tendermint/tendermint/abci/client"
  15. "github.com/tendermint/tendermint/abci/example/code"
  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/proto/tendermint/crypto"
  22. )
  23. // client is a global variable so it can be reused by the console
  24. var (
  25. client abciclient.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. // kvstore
  40. flagPersist string
  41. )
  42. var RootCmd = &cobra.Command{
  43. Use: "abci-cli",
  44. Short: "the ABCI CLI tool wraps an ABCI client",
  45. Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers",
  46. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  47. switch cmd.Use {
  48. case "kvstore", "version":
  49. return nil
  50. }
  51. if logger == nil {
  52. logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo)
  53. }
  54. if client == nil {
  55. var err error
  56. client, err = abciclient.NewClient(logger.With("module", "abci-client"), flagAddress, flagAbci, false)
  57. if err != nil {
  58. return err
  59. }
  60. if err := client.Start(cmd.Context()); err != nil {
  61. return err
  62. }
  63. }
  64. return nil
  65. },
  66. }
  67. // Structure for data passed to print response.
  68. type response struct {
  69. // generic abci response
  70. Data []byte
  71. Code uint32
  72. Info string
  73. Log string
  74. Query *queryResponse
  75. }
  76. type queryResponse struct {
  77. Key []byte
  78. Value []byte
  79. Height int64
  80. ProofOps *crypto.ProofOps
  81. }
  82. func Execute() error {
  83. addGlobalFlags()
  84. addCommands()
  85. return RootCmd.Execute()
  86. }
  87. func addGlobalFlags() {
  88. RootCmd.PersistentFlags().StringVarP(&flagAddress,
  89. "address",
  90. "",
  91. "tcp://0.0.0.0:26658",
  92. "address of application socket")
  93. RootCmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc")
  94. RootCmd.PersistentFlags().BoolVarP(&flagVerbose,
  95. "verbose",
  96. "v",
  97. false,
  98. "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,
  105. "prove",
  106. "",
  107. false,
  108. "whether or not to return a merkle proof of the query result")
  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(deliverTxCmd)
  119. RootCmd.AddCommand(checkTxCmd)
  120. RootCmd.AddCommand(commitCmd)
  121. RootCmd.AddCommand(versionCmd)
  122. RootCmd.AddCommand(testCmd)
  123. addQueryFlags()
  124. RootCmd.AddCommand(queryCmd)
  125. // examples
  126. addKVStoreFlags()
  127. RootCmd.AddCommand(kvstoreCmd)
  128. }
  129. var batchCmd = &cobra.Command{
  130. Use: "batch",
  131. Short: "run a batch of abci commands against an application",
  132. Long: `run a batch of abci commands against an application
  133. This command is run by piping in a file containing a series of commands
  134. you'd like to run:
  135. abci-cli batch < example.file
  136. where example.file looks something like:
  137. check_tx 0x00
  138. check_tx 0xff
  139. deliver_tx 0x00
  140. check_tx 0x00
  141. deliver_tx 0x01
  142. deliver_tx 0x04
  143. info
  144. `,
  145. Args: cobra.ExactArgs(0),
  146. RunE: cmdBatch,
  147. }
  148. var consoleCmd = &cobra.Command{
  149. Use: "console",
  150. Short: "start an interactive ABCI console for multiple commands",
  151. Long: `start an interactive ABCI console for multiple commands
  152. This command opens an interactive console for running any of the other commands
  153. without opening a new connection each time
  154. `,
  155. Args: cobra.ExactArgs(0),
  156. ValidArgs: []string{"echo", "info", "deliver_tx", "check_tx", "commit", "query"},
  157. RunE: cmdConsole,
  158. }
  159. var echoCmd = &cobra.Command{
  160. Use: "echo",
  161. Short: "have the application echo a message",
  162. Long: "have the application echo a message",
  163. Args: cobra.ExactArgs(1),
  164. RunE: cmdEcho,
  165. }
  166. var infoCmd = &cobra.Command{
  167. Use: "info",
  168. Short: "get some info about the application",
  169. Long: "get some info about the application",
  170. Args: cobra.ExactArgs(0),
  171. RunE: cmdInfo,
  172. }
  173. var deliverTxCmd = &cobra.Command{
  174. Use: "deliver_tx",
  175. Short: "deliver a new transaction to the application",
  176. Long: "deliver a new transaction to the application",
  177. Args: cobra.ExactArgs(1),
  178. RunE: cmdDeliverTx,
  179. }
  180. var checkTxCmd = &cobra.Command{
  181. Use: "check_tx",
  182. Short: "validate a transaction",
  183. Long: "validate a transaction",
  184. Args: cobra.ExactArgs(1),
  185. RunE: cmdCheckTx,
  186. }
  187. var commitCmd = &cobra.Command{
  188. Use: "commit",
  189. Short: "commit the application state and return the Merkle root hash",
  190. Long: "commit the application state and return the Merkle root hash",
  191. Args: cobra.ExactArgs(0),
  192. RunE: cmdCommit,
  193. }
  194. var versionCmd = &cobra.Command{
  195. Use: "version",
  196. Short: "print ABCI console version",
  197. Long: "print ABCI console version",
  198. Args: cobra.ExactArgs(0),
  199. RunE: func(cmd *cobra.Command, args []string) error {
  200. fmt.Println(version.Version)
  201. return nil
  202. },
  203. }
  204. var queryCmd = &cobra.Command{
  205. Use: "query",
  206. Short: "query the application state",
  207. Long: "query the application state",
  208. Args: cobra.ExactArgs(1),
  209. RunE: cmdQuery,
  210. }
  211. var kvstoreCmd = &cobra.Command{
  212. Use: "kvstore",
  213. Short: "ABCI demo example",
  214. Long: "ABCI demo example",
  215. Args: cobra.ExactArgs(0),
  216. RunE: cmdKVStore,
  217. }
  218. var testCmd = &cobra.Command{
  219. Use: "test",
  220. Short: "run integration tests",
  221. Long: "run integration tests",
  222. Args: cobra.ExactArgs(0),
  223. RunE: cmdTest,
  224. }
  225. // Generates new Args array based off of previous call args to maintain flag persistence
  226. func persistentArgs(line []byte) []string {
  227. // generate the arguments to run from original os.Args
  228. // to maintain flag arguments
  229. args := os.Args
  230. args = args[:len(args)-1] // remove the previous command argument
  231. if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors
  232. args = append(args, strings.Split(string(line), " ")...)
  233. }
  234. return args
  235. }
  236. //--------------------------------------------------------------------------------
  237. func compose(fs []func() error) error {
  238. if len(fs) == 0 {
  239. return nil
  240. }
  241. err := fs[0]()
  242. if err == nil {
  243. return compose(fs[1:])
  244. }
  245. return err
  246. }
  247. func cmdTest(cmd *cobra.Command, args []string) error {
  248. ctx := cmd.Context()
  249. return compose(
  250. []func() error{
  251. func() error { return servertest.InitChain(ctx, client) },
  252. func() error { return servertest.Commit(ctx, client, nil) },
  253. func() error { return servertest.DeliverTx(ctx, client, []byte("abc"), code.CodeTypeBadNonce, nil) },
  254. func() error { return servertest.Commit(ctx, client, nil) },
  255. func() error { return servertest.DeliverTx(ctx, client, []byte{0x00}, code.CodeTypeOK, nil) },
  256. func() error { return servertest.Commit(ctx, client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) },
  257. func() error { return servertest.DeliverTx(ctx, client, []byte{0x00}, code.CodeTypeBadNonce, nil) },
  258. func() error { return servertest.DeliverTx(ctx, client, []byte{0x01}, code.CodeTypeOK, nil) },
  259. func() error { return servertest.DeliverTx(ctx, client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) },
  260. func() error { return servertest.DeliverTx(ctx, client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) },
  261. func() error { return servertest.DeliverTx(ctx, client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) },
  262. func() error {
  263. return servertest.DeliverTx(ctx, client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil)
  264. },
  265. func() error { return servertest.Commit(ctx, client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) },
  266. })
  267. }
  268. func cmdBatch(cmd *cobra.Command, args []string) error {
  269. bufReader := bufio.NewReader(os.Stdin)
  270. LOOP:
  271. for {
  272. line, more, err := bufReader.ReadLine()
  273. switch {
  274. case more:
  275. return errors.New("input line is too long")
  276. case err == io.EOF:
  277. break LOOP
  278. case len(line) == 0:
  279. continue
  280. case err != nil:
  281. return err
  282. }
  283. cmdArgs := persistentArgs(line)
  284. if err := muxOnCommands(cmd, cmdArgs); err != nil {
  285. return err
  286. }
  287. fmt.Println()
  288. }
  289. return nil
  290. }
  291. func cmdConsole(cmd *cobra.Command, args []string) error {
  292. for {
  293. fmt.Printf("> ")
  294. bufReader := bufio.NewReader(os.Stdin)
  295. line, more, err := bufReader.ReadLine()
  296. if more {
  297. return errors.New("input is too long")
  298. } else if err != nil {
  299. return err
  300. }
  301. pArgs := persistentArgs(line)
  302. if err := muxOnCommands(cmd, pArgs); err != nil {
  303. return err
  304. }
  305. }
  306. }
  307. func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
  308. if len(pArgs) < 2 {
  309. return errors.New("expecting persistent args of the form: abci-cli [command] <...>")
  310. }
  311. // TODO: this parsing is fragile
  312. args := []string{}
  313. for i := 0; i < len(pArgs); i++ {
  314. arg := pArgs[i]
  315. // check for flags
  316. if strings.HasPrefix(arg, "-") {
  317. // if it has an equal, we can just skip
  318. if strings.Contains(arg, "=") {
  319. continue
  320. }
  321. // if its a boolean, we can just skip
  322. _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-"))
  323. if err == nil {
  324. continue
  325. }
  326. // otherwise, we need to skip the next one too
  327. i++
  328. continue
  329. }
  330. // append the actual arg
  331. args = append(args, arg)
  332. }
  333. var subCommand string
  334. var actualArgs []string
  335. if len(args) > 1 {
  336. subCommand = args[1]
  337. }
  338. if len(args) > 2 {
  339. actualArgs = args[2:]
  340. }
  341. cmd.Use = subCommand // for later print statements ...
  342. switch strings.ToLower(subCommand) {
  343. case "check_tx":
  344. return cmdCheckTx(cmd, actualArgs)
  345. case "commit":
  346. return cmdCommit(cmd, actualArgs)
  347. case "deliver_tx":
  348. return cmdDeliverTx(cmd, actualArgs)
  349. case "echo":
  350. return cmdEcho(cmd, actualArgs)
  351. case "info":
  352. return cmdInfo(cmd, actualArgs)
  353. case "query":
  354. return cmdQuery(cmd, actualArgs)
  355. default:
  356. return cmdUnimplemented(cmd, pArgs)
  357. }
  358. }
  359. func cmdUnimplemented(cmd *cobra.Command, args []string) error {
  360. msg := "unimplemented command"
  361. if len(args) > 0 {
  362. msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
  363. }
  364. printResponse(cmd, args, response{
  365. Code: codeBad,
  366. Log: msg,
  367. })
  368. fmt.Println("Available commands:")
  369. fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
  370. fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
  371. fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
  372. fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
  373. fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
  374. fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
  375. fmt.Println("Use \"[command] --help\" for more information about a command.")
  376. return nil
  377. }
  378. // Have the application echo a message
  379. func cmdEcho(cmd *cobra.Command, args []string) error {
  380. msg := ""
  381. if len(args) > 0 {
  382. msg = args[0]
  383. }
  384. res, err := client.EchoSync(cmd.Context(), msg)
  385. if err != nil {
  386. return err
  387. }
  388. printResponse(cmd, args, response{
  389. Data: []byte(res.Message),
  390. })
  391. return nil
  392. }
  393. // Get some info from the application
  394. func cmdInfo(cmd *cobra.Command, args []string) error {
  395. var version string
  396. if len(args) == 1 {
  397. version = args[0]
  398. }
  399. res, err := client.InfoSync(cmd.Context(), types.RequestInfo{Version: version})
  400. if err != nil {
  401. return err
  402. }
  403. printResponse(cmd, args, response{
  404. Data: []byte(res.Data),
  405. })
  406. return nil
  407. }
  408. const codeBad uint32 = 10
  409. // Append a new tx to application
  410. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  411. if len(args) == 0 {
  412. printResponse(cmd, args, response{
  413. Code: codeBad,
  414. Log: "want the tx",
  415. })
  416. return nil
  417. }
  418. txBytes, err := stringOrHexToBytes(args[0])
  419. if err != nil {
  420. return err
  421. }
  422. res, err := client.DeliverTxSync(cmd.Context(), types.RequestDeliverTx{Tx: txBytes})
  423. if err != nil {
  424. return err
  425. }
  426. printResponse(cmd, args, response{
  427. Code: res.Code,
  428. Data: res.Data,
  429. Info: res.Info,
  430. Log: res.Log,
  431. })
  432. return nil
  433. }
  434. // Validate a tx
  435. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  436. if len(args) == 0 {
  437. printResponse(cmd, args, response{
  438. Code: codeBad,
  439. Info: "want the tx",
  440. })
  441. return nil
  442. }
  443. txBytes, err := stringOrHexToBytes(args[0])
  444. if err != nil {
  445. return err
  446. }
  447. res, err := client.CheckTxSync(cmd.Context(), types.RequestCheckTx{Tx: txBytes})
  448. if err != nil {
  449. return err
  450. }
  451. printResponse(cmd, args, response{
  452. Code: res.Code,
  453. Data: res.Data,
  454. Info: res.Info,
  455. Log: res.Log,
  456. })
  457. return nil
  458. }
  459. // Get application Merkle root hash
  460. func cmdCommit(cmd *cobra.Command, args []string) error {
  461. res, err := client.CommitSync(cmd.Context())
  462. if err != nil {
  463. return err
  464. }
  465. printResponse(cmd, args, response{
  466. Data: res.Data,
  467. })
  468. return nil
  469. }
  470. // Query application state
  471. func cmdQuery(cmd *cobra.Command, args []string) error {
  472. if len(args) == 0 {
  473. printResponse(cmd, args, response{
  474. Code: codeBad,
  475. Info: "want the query",
  476. Log: "",
  477. })
  478. return nil
  479. }
  480. queryBytes, err := stringOrHexToBytes(args[0])
  481. if err != nil {
  482. return err
  483. }
  484. resQuery, err := client.QuerySync(cmd.Context(), types.RequestQuery{
  485. Data: queryBytes,
  486. Path: flagPath,
  487. Height: int64(flagHeight),
  488. Prove: flagProve,
  489. })
  490. if err != nil {
  491. return err
  492. }
  493. printResponse(cmd, args, response{
  494. Code: resQuery.Code,
  495. Info: resQuery.Info,
  496. Log: resQuery.Log,
  497. Query: &queryResponse{
  498. Key: resQuery.Key,
  499. Value: resQuery.Value,
  500. Height: resQuery.Height,
  501. ProofOps: resQuery.ProofOps,
  502. },
  503. })
  504. return nil
  505. }
  506. func cmdKVStore(cmd *cobra.Command, args []string) error {
  507. logger := log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo)
  508. // Create the application - in memory or persisted to disk
  509. var app types.Application
  510. if flagPersist == "" {
  511. app = kvstore.NewApplication()
  512. } else {
  513. app = kvstore.NewPersistentKVStoreApplication(logger, flagPersist)
  514. }
  515. // Start the listener
  516. srv, err := server.NewServer(logger.With("module", "abci-server"), flagAddress, flagAbci, app)
  517. if err != nil {
  518. return err
  519. }
  520. ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM)
  521. defer cancel()
  522. if err := srv.Start(ctx); err != nil {
  523. return err
  524. }
  525. // Run forever.
  526. <-ctx.Done()
  527. return nil
  528. }
  529. //--------------------------------------------------------------------------------
  530. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  531. if flagVerbose {
  532. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  533. }
  534. // Always print the status code.
  535. if rsp.Code == types.CodeTypeOK {
  536. fmt.Printf("-> code: OK\n")
  537. } else {
  538. fmt.Printf("-> code: %d\n", rsp.Code)
  539. }
  540. if len(rsp.Data) != 0 {
  541. // Do no print this line when using the commit command
  542. // because the string comes out as gibberish
  543. if cmd.Use != "commit" {
  544. fmt.Printf("-> data: %s\n", rsp.Data)
  545. }
  546. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  547. }
  548. if rsp.Log != "" {
  549. fmt.Printf("-> log: %s\n", rsp.Log)
  550. }
  551. if rsp.Query != nil {
  552. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  553. if rsp.Query.Key != nil {
  554. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  555. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  556. }
  557. if rsp.Query.Value != nil {
  558. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  559. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  560. }
  561. if rsp.Query.ProofOps != nil {
  562. fmt.Printf("-> proof: %#v\n", rsp.Query.ProofOps)
  563. }
  564. }
  565. }
  566. // NOTE: s is interpreted as a string unless prefixed with 0x
  567. func stringOrHexToBytes(s string) ([]byte, error) {
  568. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  569. b, err := hex.DecodeString(s[2:])
  570. if err != nil {
  571. err = fmt.Errorf("error decoding hex argument: %s", err.Error())
  572. return nil, err
  573. }
  574. return b, nil
  575. }
  576. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  577. err := fmt.Errorf("invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  578. return nil, err
  579. }
  580. return []byte(s[1 : len(s)-1]), nil
  581. }