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.

632 lines
15 KiB

9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
8 years ago
9 years ago
8 years ago
7 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
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. "os/exec"
  10. "strings"
  11. "github.com/spf13/cobra"
  12. cmn "github.com/tendermint/tmlibs/common"
  13. "github.com/tendermint/tmlibs/log"
  14. abcicli "github.com/tendermint/abci/client"
  15. "github.com/tendermint/abci/example/code"
  16. "github.com/tendermint/abci/example/counter"
  17. "github.com/tendermint/abci/example/dummy"
  18. "github.com/tendermint/abci/server"
  19. servertest "github.com/tendermint/abci/tests/server"
  20. "github.com/tendermint/abci/types"
  21. "github.com/tendermint/abci/version"
  22. )
  23. // client is a global variable so it can be reused by the console
  24. var (
  25. client abcicli.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. // counter
  40. flagAddrC string
  41. flagSerial bool
  42. // dummy
  43. flagAddrD string
  44. flagPersist string
  45. )
  46. var RootCmd = &cobra.Command{
  47. Use: "abci-cli",
  48. Short: "",
  49. Long: "",
  50. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  51. switch cmd.Use {
  52. case "counter", "dummy": // for the examples apps, don't pre-run
  53. return nil
  54. case "version": // skip running for version command
  55. return nil
  56. }
  57. if logger == nil {
  58. allowLevel, err := log.AllowLevel(flagLogLevel)
  59. if err != nil {
  60. return err
  61. }
  62. logger = log.NewFilter(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), allowLevel)
  63. }
  64. if client == nil {
  65. var err error
  66. client, err = abcicli.NewClient(flagAddress, flagAbci, false)
  67. if err != nil {
  68. return err
  69. }
  70. client.SetLogger(logger.With("module", "abci-client"))
  71. if err := client.Start(); err != nil {
  72. return err
  73. }
  74. }
  75. return nil
  76. },
  77. }
  78. // Structure for data passed to print response.
  79. type response struct {
  80. // generic abci response
  81. Data []byte
  82. Code uint32
  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: "",
  139. Args: cobra.ExactArgs(0),
  140. RunE: func(cmd *cobra.Command, args []string) error {
  141. return cmdBatch(cmd, args)
  142. },
  143. }
  144. var consoleCmd = &cobra.Command{
  145. Use: "console",
  146. Short: "Start an interactive abci console for multiple commands",
  147. Long: "",
  148. Args: cobra.ExactArgs(0),
  149. ValidArgs: []string{"batch", "echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"},
  150. RunE: func(cmd *cobra.Command, args []string) error {
  151. return cmdConsole(cmd, args)
  152. },
  153. }
  154. var echoCmd = &cobra.Command{
  155. Use: "echo",
  156. Short: "Have the application echo a message",
  157. Long: "",
  158. Args: cobra.ExactArgs(1),
  159. RunE: func(cmd *cobra.Command, args []string) error {
  160. return cmdEcho(cmd, args)
  161. },
  162. }
  163. var infoCmd = &cobra.Command{
  164. Use: "info",
  165. Short: "Get some info about the application",
  166. Long: "",
  167. Args: cobra.ExactArgs(0),
  168. RunE: func(cmd *cobra.Command, args []string) error {
  169. return cmdInfo(cmd, args)
  170. },
  171. }
  172. var setOptionCmd = &cobra.Command{
  173. Use: "set_option",
  174. Short: "Set an option on the application",
  175. Long: "",
  176. Args: cobra.ExactArgs(2),
  177. RunE: func(cmd *cobra.Command, args []string) error {
  178. return cmdSetOption(cmd, args)
  179. },
  180. }
  181. var deliverTxCmd = &cobra.Command{
  182. Use: "deliver_tx",
  183. Short: "Deliver a new transaction to the application",
  184. Long: "",
  185. Args: cobra.ExactArgs(1),
  186. RunE: func(cmd *cobra.Command, args []string) error {
  187. return cmdDeliverTx(cmd, args)
  188. },
  189. }
  190. var checkTxCmd = &cobra.Command{
  191. Use: "check_tx",
  192. Short: "Validate a transaction",
  193. Long: "",
  194. Args: cobra.ExactArgs(1),
  195. RunE: func(cmd *cobra.Command, args []string) error {
  196. return cmdCheckTx(cmd, args)
  197. },
  198. }
  199. var commitCmd = &cobra.Command{
  200. Use: "commit",
  201. Short: "Commit the application state and return the Merkle root hash",
  202. Long: "",
  203. Args: cobra.ExactArgs(0),
  204. RunE: func(cmd *cobra.Command, args []string) error {
  205. return cmdCommit(cmd, args)
  206. },
  207. }
  208. var versionCmd = &cobra.Command{
  209. Use: "version",
  210. Short: "Print abci console version",
  211. Long: "",
  212. Args: cobra.ExactArgs(0),
  213. RunE: func(cmd *cobra.Command, args []string) error {
  214. fmt.Println(version.Version)
  215. return nil
  216. },
  217. }
  218. var queryCmd = &cobra.Command{
  219. Use: "query",
  220. Short: "Query the application state",
  221. Long: "",
  222. Args: cobra.ExactArgs(1),
  223. RunE: func(cmd *cobra.Command, args []string) error {
  224. return cmdQuery(cmd, args)
  225. },
  226. }
  227. var counterCmd = &cobra.Command{
  228. Use: "counter",
  229. Short: "ABCI demo example",
  230. Long: "",
  231. Args: cobra.ExactArgs(0),
  232. RunE: func(cmd *cobra.Command, args []string) error {
  233. return cmdCounter(cmd, args)
  234. },
  235. }
  236. var dummyCmd = &cobra.Command{
  237. Use: "dummy",
  238. Short: "ABCI demo example",
  239. Long: "",
  240. Args: cobra.ExactArgs(0),
  241. RunE: func(cmd *cobra.Command, args []string) error {
  242. return cmdDummy(cmd, args)
  243. },
  244. }
  245. var testCmd = &cobra.Command{
  246. Use: "test",
  247. Short: "Run integration tests",
  248. Long: "",
  249. Args: cobra.ExactArgs(0),
  250. RunE: func(cmd *cobra.Command, args []string) error {
  251. return cmdTest(cmd, args)
  252. },
  253. }
  254. // Generates new Args array based off of previous call args to maintain flag persistence
  255. func persistentArgs(line []byte) []string {
  256. // generate the arguments to run from original os.Args
  257. // to maintain flag arguments
  258. args := os.Args
  259. args = args[:len(args)-1] // remove the previous command argument
  260. if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors
  261. args = append(args, strings.Split(string(line), " ")...)
  262. }
  263. return args
  264. }
  265. //--------------------------------------------------------------------------------
  266. func or(err1 error, err2 error) error {
  267. if err1 == nil {
  268. return err2
  269. } else {
  270. return err1
  271. }
  272. }
  273. func cmdTest(cmd *cobra.Command, args []string) error {
  274. fmt.Println("Running tests")
  275. var err error
  276. err = servertest.InitChain(client)
  277. fmt.Println("")
  278. err = or(err, servertest.SetOption(client, "serial", "on"))
  279. fmt.Println("")
  280. err = or(err, servertest.Commit(client, nil))
  281. fmt.Println("")
  282. err = or(err, servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil))
  283. fmt.Println("")
  284. err = or(err, servertest.Commit(client, nil))
  285. fmt.Println("")
  286. err = or(err, servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil))
  287. fmt.Println("")
  288. err = or(err, servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}))
  289. fmt.Println("")
  290. err = or(err, servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil))
  291. fmt.Println("")
  292. err = or(err, servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil))
  293. fmt.Println("")
  294. err = or(err, servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil))
  295. fmt.Println("")
  296. err = or(err, servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil))
  297. fmt.Println("")
  298. err = or(err, servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil))
  299. fmt.Println("")
  300. err = or(err, servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil))
  301. fmt.Println("")
  302. err = or(err, servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}))
  303. if err != nil {
  304. return errors.New("Some checks didn't pass, please inspect stdout to see the exact failures.")
  305. }
  306. return nil
  307. }
  308. func cmdBatch(cmd *cobra.Command, args []string) error {
  309. bufReader := bufio.NewReader(os.Stdin)
  310. for {
  311. line, more, err := bufReader.ReadLine()
  312. if more {
  313. return errors.New("Input line is too long")
  314. } else if err == io.EOF {
  315. break
  316. } else if len(line) == 0 {
  317. continue
  318. } else if err != nil {
  319. return err
  320. }
  321. pArgs := persistentArgs(line)
  322. out, err := exec.Command(pArgs[0], pArgs[1:]...).Output() // nolint: gas
  323. if err != nil {
  324. return err
  325. }
  326. fmt.Println(string(out))
  327. }
  328. return nil
  329. }
  330. func cmdConsole(cmd *cobra.Command, args []string) error {
  331. for {
  332. fmt.Printf("> ")
  333. bufReader := bufio.NewReader(os.Stdin)
  334. line, more, err := bufReader.ReadLine()
  335. if more {
  336. return errors.New("Input is too long")
  337. } else if err != nil {
  338. return err
  339. }
  340. pArgs := persistentArgs(line)
  341. out, err := exec.Command(pArgs[0], pArgs[1:]...).Output() // nolint: gas
  342. if err != nil {
  343. return err
  344. }
  345. fmt.Println(string(out))
  346. }
  347. return nil
  348. }
  349. // Have the application echo a message
  350. func cmdEcho(cmd *cobra.Command, args []string) error {
  351. res, err := client.EchoSync(args[0])
  352. if err != nil {
  353. return err
  354. }
  355. printResponse(cmd, args, response{
  356. Data: []byte(res.Message),
  357. })
  358. return nil
  359. }
  360. // Get some info from the application
  361. func cmdInfo(cmd *cobra.Command, args []string) error {
  362. var version string
  363. if len(args) == 1 {
  364. version = args[0]
  365. }
  366. res, err := client.InfoSync(types.RequestInfo{version})
  367. if err != nil {
  368. return err
  369. }
  370. printResponse(cmd, args, response{
  371. Data: []byte(res.Data),
  372. })
  373. return nil
  374. }
  375. // Set an option on the application
  376. func cmdSetOption(cmd *cobra.Command, args []string) error {
  377. key, val := args[0], args[1]
  378. res, err := client.SetOptionSync(types.RequestSetOption{key, val})
  379. if err != nil {
  380. return err
  381. }
  382. printResponse(cmd, args, response{
  383. Code: res.Code,
  384. Log: res.Log,
  385. })
  386. return nil
  387. }
  388. // Append a new tx to application
  389. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  390. txBytes, err := stringOrHexToBytes(args[0])
  391. if err != nil {
  392. return err
  393. }
  394. res, err := client.DeliverTxSync(txBytes)
  395. if err != nil {
  396. return err
  397. }
  398. printResponse(cmd, args, response{
  399. Code: res.Code,
  400. Data: res.Data,
  401. Log: res.Log,
  402. })
  403. return nil
  404. }
  405. // Validate a tx
  406. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  407. txBytes, err := stringOrHexToBytes(args[0])
  408. if err != nil {
  409. return err
  410. }
  411. res, err := client.CheckTxSync(txBytes)
  412. if err != nil {
  413. return err
  414. }
  415. printResponse(cmd, args, response{
  416. Code: res.Code,
  417. Data: res.Data,
  418. Log: res.Log,
  419. })
  420. return nil
  421. }
  422. // Get application Merkle root hash
  423. func cmdCommit(cmd *cobra.Command, args []string) error {
  424. res, err := client.CommitSync()
  425. if err != nil {
  426. return err
  427. }
  428. printResponse(cmd, args, response{
  429. Code: res.Code,
  430. Data: res.Data,
  431. Log: res.Log,
  432. })
  433. return nil
  434. }
  435. // Query application state
  436. func cmdQuery(cmd *cobra.Command, args []string) error {
  437. queryBytes, err := stringOrHexToBytes(args[0])
  438. if err != nil {
  439. return err
  440. }
  441. resQuery, err := client.QuerySync(types.RequestQuery{
  442. Data: queryBytes,
  443. Path: flagPath,
  444. Height: int64(flagHeight),
  445. Prove: flagProve,
  446. })
  447. if err != nil {
  448. return err
  449. }
  450. printResponse(cmd, args, response{
  451. Code: resQuery.Code,
  452. Log: resQuery.Log,
  453. Query: &queryResponse{
  454. Key: resQuery.Key,
  455. Value: resQuery.Value,
  456. Height: resQuery.Height,
  457. Proof: resQuery.Proof,
  458. },
  459. })
  460. return nil
  461. }
  462. func cmdCounter(cmd *cobra.Command, args []string) error {
  463. app := counter.NewCounterApplication(flagSerial)
  464. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  465. // Start the listener
  466. srv, err := server.NewServer(flagAddrC, flagAbci, app)
  467. if err != nil {
  468. return err
  469. }
  470. srv.SetLogger(logger.With("module", "abci-server"))
  471. if err := srv.Start(); err != nil {
  472. return err
  473. }
  474. // Wait forever
  475. cmn.TrapSignal(func() {
  476. // Cleanup
  477. srv.Stop()
  478. })
  479. return nil
  480. }
  481. func cmdDummy(cmd *cobra.Command, args []string) error {
  482. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  483. // Create the application - in memory or persisted to disk
  484. var app types.Application
  485. if flagPersist == "" {
  486. app = dummy.NewDummyApplication()
  487. } else {
  488. app = dummy.NewPersistentDummyApplication(flagPersist)
  489. app.(*dummy.PersistentDummyApplication).SetLogger(logger.With("module", "dummy"))
  490. }
  491. // Start the listener
  492. srv, err := server.NewServer(flagAddrD, flagAbci, app)
  493. if err != nil {
  494. return err
  495. }
  496. srv.SetLogger(logger.With("module", "abci-server"))
  497. if err := srv.Start(); err != nil {
  498. return err
  499. }
  500. // Wait forever
  501. cmn.TrapSignal(func() {
  502. // Cleanup
  503. srv.Stop()
  504. })
  505. return nil
  506. }
  507. //--------------------------------------------------------------------------------
  508. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  509. if flagVerbose {
  510. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  511. }
  512. // Always print the status code.
  513. if rsp.Code == types.CodeTypeOK {
  514. fmt.Printf("-> code: OK\n")
  515. } else {
  516. fmt.Printf("-> code: %d\n", rsp.Code)
  517. }
  518. if len(rsp.Data) != 0 {
  519. // Do no print this line when using the commit command
  520. // because the string comes out as gibberish
  521. if cmd.Use != "commit" {
  522. fmt.Printf("-> data: %s\n", rsp.Data)
  523. }
  524. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  525. }
  526. if rsp.Log != "" {
  527. fmt.Printf("-> log: %s\n", rsp.Log)
  528. }
  529. if rsp.Query != nil {
  530. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  531. if rsp.Query.Key != nil {
  532. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  533. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  534. }
  535. if rsp.Query.Value != nil {
  536. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  537. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  538. }
  539. if rsp.Query.Proof != nil {
  540. fmt.Printf("-> proof: %X\n", rsp.Query.Proof)
  541. }
  542. }
  543. }
  544. // NOTE: s is interpreted as a string unless prefixed with 0x
  545. func stringOrHexToBytes(s string) ([]byte, error) {
  546. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  547. b, err := hex.DecodeString(s[2:])
  548. if err != nil {
  549. err = fmt.Errorf("Error decoding hex argument: %s", err.Error())
  550. return nil, err
  551. }
  552. return b, nil
  553. }
  554. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  555. err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  556. return nil, err
  557. }
  558. return []byte(s[1 : len(s)-1]), nil
  559. }