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.

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