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.

725 lines
17 KiB

9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
8 years ago
9 years ago
9 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 compose(fs []func() error) error {
  266. if len(fs) == 0 {
  267. return nil
  268. } else {
  269. err := fs[0]()
  270. if err == nil {
  271. return compose(fs[1:])
  272. } else {
  273. return err
  274. }
  275. }
  276. }
  277. func cmdTest(cmd *cobra.Command, args []string) error {
  278. return compose(
  279. []func() error{
  280. func() error { return servertest.InitChain(client) },
  281. func() error { return servertest.SetOption(client, "serial", "on") },
  282. func() error { return servertest.Commit(client, nil) },
  283. func() error { return servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) },
  284. func() error { return servertest.Commit(client, nil) },
  285. func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil) },
  286. func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) },
  287. func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) },
  288. func() error { return servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil) },
  289. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) },
  290. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) },
  291. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) },
  292. func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil) },
  293. func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5})},
  294. })
  295. }
  296. func cmdBatch(cmd *cobra.Command, args []string) error {
  297. bufReader := bufio.NewReader(os.Stdin)
  298. for {
  299. line, more, err := bufReader.ReadLine()
  300. if more {
  301. return errors.New("Input line is too long")
  302. } else if err == io.EOF {
  303. break
  304. } else if len(line) == 0 {
  305. continue
  306. } else if err != nil {
  307. return err
  308. }
  309. cmdArgs := persistentArgs(line)
  310. if err := muxOnCommands(cmd, cmdArgs); err != nil {
  311. return err
  312. }
  313. fmt.Println()
  314. }
  315. return nil
  316. }
  317. func cmdConsole(cmd *cobra.Command, args []string) error {
  318. for {
  319. fmt.Printf("> ")
  320. bufReader := bufio.NewReader(os.Stdin)
  321. line, more, err := bufReader.ReadLine()
  322. if more {
  323. return errors.New("Input is too long")
  324. } else if err != nil {
  325. return err
  326. }
  327. pArgs := persistentArgs(line)
  328. if err := muxOnCommands(cmd, pArgs); err != nil {
  329. return err
  330. }
  331. }
  332. return nil
  333. }
  334. func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
  335. if len(pArgs) < 2 {
  336. return errors.New("expecting persistent args of the form: abci-cli [command] <...>")
  337. }
  338. // TODO: this parsing is fragile
  339. args := []string{}
  340. for i := 0; i < len(pArgs); i++ {
  341. arg := pArgs[i]
  342. // check for flags
  343. if strings.HasPrefix(arg, "-") {
  344. // if it has an equal, we can just skip
  345. if strings.Contains(arg, "=") {
  346. continue
  347. }
  348. // if its a boolean, we can just skip
  349. _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-"))
  350. if err == nil {
  351. continue
  352. }
  353. // otherwise, we need to skip the next one too
  354. i += 1
  355. continue
  356. }
  357. // append the actual arg
  358. args = append(args, arg)
  359. }
  360. var subCommand string
  361. var actualArgs []string
  362. if len(args) > 1 {
  363. subCommand = args[1]
  364. }
  365. if len(args) > 2 {
  366. actualArgs = args[2:]
  367. }
  368. cmd.Use = subCommand // for later print statements ...
  369. switch strings.ToLower(subCommand) {
  370. case "check_tx":
  371. return cmdCheckTx(cmd, actualArgs)
  372. case "commit":
  373. return cmdCommit(cmd, actualArgs)
  374. case "deliver_tx":
  375. return cmdDeliverTx(cmd, actualArgs)
  376. case "echo":
  377. return cmdEcho(cmd, actualArgs)
  378. case "info":
  379. return cmdInfo(cmd, actualArgs)
  380. case "query":
  381. return cmdQuery(cmd, actualArgs)
  382. case "set_option":
  383. return cmdSetOption(cmd, actualArgs)
  384. default:
  385. return cmdUnimplemented(cmd, pArgs)
  386. }
  387. }
  388. func cmdUnimplemented(cmd *cobra.Command, args []string) error {
  389. // TODO: Print out all the sub-commands available
  390. msg := "unimplemented command"
  391. if err := cmd.Help(); err != nil {
  392. msg = err.Error()
  393. }
  394. if len(args) > 0 {
  395. msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
  396. }
  397. printResponse(cmd, args, response{
  398. Code: codeBad,
  399. Log: msg,
  400. })
  401. return nil
  402. }
  403. // Have the application echo a message
  404. func cmdEcho(cmd *cobra.Command, args []string) error {
  405. msg := ""
  406. if len(args) > 0 {
  407. msg = args[0]
  408. }
  409. res, err := client.EchoSync(msg)
  410. if err != nil {
  411. return err
  412. }
  413. printResponse(cmd, args, response{
  414. Data: []byte(res.Message),
  415. })
  416. return nil
  417. }
  418. // Get some info from the application
  419. func cmdInfo(cmd *cobra.Command, args []string) error {
  420. var version string
  421. if len(args) == 1 {
  422. version = args[0]
  423. }
  424. res, err := client.InfoSync(types.RequestInfo{version})
  425. if err != nil {
  426. return err
  427. }
  428. printResponse(cmd, args, response{
  429. Data: []byte(res.Data),
  430. })
  431. return nil
  432. }
  433. const codeBad uint32 = 10
  434. // Set an option on the application
  435. func cmdSetOption(cmd *cobra.Command, args []string) error {
  436. if len(args) < 2 {
  437. printResponse(cmd, args, response{
  438. Code: codeBad,
  439. Log: "want at least arguments of the form: <key> <value>",
  440. })
  441. return nil
  442. }
  443. key, val := args[0], args[1]
  444. res, err := client.SetOptionSync(types.RequestSetOption{key, val})
  445. if err != nil {
  446. return err
  447. }
  448. printResponse(cmd, args, response{
  449. Code: res.Code,
  450. Log: res.Log,
  451. })
  452. return nil
  453. }
  454. // Append a new tx to application
  455. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  456. if len(args) == 0 {
  457. printResponse(cmd, args, response{
  458. Code: codeBad,
  459. Log: "want the tx",
  460. })
  461. return nil
  462. }
  463. txBytes, err := stringOrHexToBytes(args[0])
  464. if err != nil {
  465. return err
  466. }
  467. res, err := client.DeliverTxSync(txBytes)
  468. if err != nil {
  469. return err
  470. }
  471. printResponse(cmd, args, response{
  472. Code: res.Code,
  473. Data: res.Data,
  474. Log: res.Log,
  475. })
  476. return nil
  477. }
  478. // Validate a tx
  479. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  480. if len(args) == 0 {
  481. printResponse(cmd, args, response{
  482. Code: codeBad,
  483. Log: "want the tx",
  484. })
  485. return nil
  486. }
  487. txBytes, err := stringOrHexToBytes(args[0])
  488. if err != nil {
  489. return err
  490. }
  491. res, err := client.CheckTxSync(txBytes)
  492. if err != nil {
  493. return err
  494. }
  495. printResponse(cmd, args, response{
  496. Code: res.Code,
  497. Data: res.Data,
  498. Log: res.Log,
  499. })
  500. return nil
  501. }
  502. // Get application Merkle root hash
  503. func cmdCommit(cmd *cobra.Command, args []string) error {
  504. res, err := client.CommitSync()
  505. if err != nil {
  506. return err
  507. }
  508. printResponse(cmd, args, response{
  509. Code: res.Code,
  510. Data: res.Data,
  511. Log: res.Log,
  512. })
  513. return nil
  514. }
  515. // Query application state
  516. func cmdQuery(cmd *cobra.Command, args []string) error {
  517. if len(args) == 0 {
  518. printResponse(cmd, args, response{
  519. Code: codeBad,
  520. Log: "want the query",
  521. })
  522. return nil
  523. }
  524. queryBytes, err := stringOrHexToBytes(args[0])
  525. if err != nil {
  526. return err
  527. }
  528. resQuery, err := client.QuerySync(types.RequestQuery{
  529. Data: queryBytes,
  530. Path: flagPath,
  531. Height: int64(flagHeight),
  532. Prove: flagProve,
  533. })
  534. if err != nil {
  535. return err
  536. }
  537. printResponse(cmd, args, response{
  538. Code: resQuery.Code,
  539. Log: resQuery.Log,
  540. Query: &queryResponse{
  541. Key: resQuery.Key,
  542. Value: resQuery.Value,
  543. Height: resQuery.Height,
  544. Proof: resQuery.Proof,
  545. },
  546. })
  547. return nil
  548. }
  549. func cmdCounter(cmd *cobra.Command, args []string) error {
  550. app := counter.NewCounterApplication(flagSerial)
  551. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  552. // Start the listener
  553. srv, err := server.NewServer(flagAddrC, flagAbci, app)
  554. if err != nil {
  555. return err
  556. }
  557. srv.SetLogger(logger.With("module", "abci-server"))
  558. if err := srv.Start(); err != nil {
  559. return err
  560. }
  561. // Wait forever
  562. cmn.TrapSignal(func() {
  563. // Cleanup
  564. srv.Stop()
  565. })
  566. return nil
  567. }
  568. func cmdDummy(cmd *cobra.Command, args []string) error {
  569. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  570. // Create the application - in memory or persisted to disk
  571. var app types.Application
  572. if flagPersist == "" {
  573. app = dummy.NewDummyApplication()
  574. } else {
  575. app = dummy.NewPersistentDummyApplication(flagPersist)
  576. app.(*dummy.PersistentDummyApplication).SetLogger(logger.With("module", "dummy"))
  577. }
  578. // Start the listener
  579. srv, err := server.NewServer(flagAddrD, flagAbci, app)
  580. if err != nil {
  581. return err
  582. }
  583. srv.SetLogger(logger.With("module", "abci-server"))
  584. if err := srv.Start(); err != nil {
  585. return err
  586. }
  587. // Wait forever
  588. cmn.TrapSignal(func() {
  589. // Cleanup
  590. srv.Stop()
  591. })
  592. return nil
  593. }
  594. //--------------------------------------------------------------------------------
  595. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  596. if flagVerbose {
  597. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  598. }
  599. // Always print the status code.
  600. if rsp.Code == types.CodeTypeOK {
  601. fmt.Printf("-> code: OK\n")
  602. } else {
  603. fmt.Printf("-> code: %d\n", rsp.Code)
  604. }
  605. if len(rsp.Data) != 0 {
  606. // Do no print this line when using the commit command
  607. // because the string comes out as gibberish
  608. if cmd.Use != "commit" {
  609. fmt.Printf("-> data: %s\n", rsp.Data)
  610. }
  611. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  612. }
  613. if rsp.Log != "" {
  614. fmt.Printf("-> log: %s\n", rsp.Log)
  615. }
  616. if rsp.Query != nil {
  617. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  618. if rsp.Query.Key != nil {
  619. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  620. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  621. }
  622. if rsp.Query.Value != nil {
  623. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  624. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  625. }
  626. if rsp.Query.Proof != nil {
  627. fmt.Printf("-> proof: %X\n", rsp.Query.Proof)
  628. }
  629. }
  630. }
  631. // NOTE: s is interpreted as a string unless prefixed with 0x
  632. func stringOrHexToBytes(s string) ([]byte, error) {
  633. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  634. b, err := hex.DecodeString(s[2:])
  635. if err != nil {
  636. err = fmt.Errorf("Error decoding hex argument: %s", err.Error())
  637. return nil, err
  638. }
  639. return b, nil
  640. }
  641. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  642. err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  643. return nil, err
  644. }
  645. return []byte(s[1 : len(s)-1]), nil
  646. }