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.

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