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.

528 lines
12 KiB

9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
8 years ago
8 years ago
9 years ago
9 years ago
7 years ago
7 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
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. abcicli "github.com/tendermint/abci/client"
  12. "github.com/tendermint/abci/example/counter"
  13. "github.com/tendermint/abci/example/dummy"
  14. "github.com/tendermint/abci/server"
  15. "github.com/tendermint/abci/types"
  16. cmn "github.com/tendermint/tmlibs/common"
  17. "github.com/tendermint/tmlibs/log"
  18. "github.com/spf13/cobra"
  19. )
  20. // Structure for data passed to print response.
  21. type response struct {
  22. // generic abci response
  23. Data []byte
  24. Code types.CodeType
  25. Log string
  26. Query *queryResponse
  27. }
  28. type queryResponse struct {
  29. Key []byte
  30. Value []byte
  31. Height uint64
  32. Proof []byte
  33. }
  34. // client is a global variable so it can be reused by the console
  35. var client abcicli.Client
  36. var logger log.Logger
  37. // flags
  38. var (
  39. // global
  40. address string
  41. abci string
  42. verbose bool
  43. // query
  44. path string
  45. height int
  46. prove bool
  47. // counter
  48. addrC string
  49. serial bool
  50. // dummy
  51. addrD string
  52. persist string
  53. )
  54. var RootCmd = &cobra.Command{
  55. Use: "abci-cli",
  56. Short: "",
  57. Long: "",
  58. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  59. switch cmd.Use {
  60. // for the examples apps, don't pre-run
  61. case "counter", "dummy":
  62. return nil
  63. }
  64. if logger == nil {
  65. logger = log.NewFilter(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), log.AllowError())
  66. }
  67. if client == nil {
  68. var err error
  69. client, err = abcicli.NewClient(address, abci, false)
  70. if err != nil {
  71. return err
  72. }
  73. client.SetLogger(logger.With("module", "abci-client"))
  74. if _, err := client.Start(); err != nil {
  75. return err
  76. }
  77. }
  78. return nil
  79. },
  80. }
  81. func Execute() {
  82. addGlobalFlags()
  83. addCommands()
  84. RootCmd.Execute()
  85. }
  86. func addGlobalFlags() {
  87. RootCmd.PersistentFlags().StringVarP(&address, "address", "", "tcp://127.0.0.1:46658", "Address of application socket")
  88. RootCmd.PersistentFlags().StringVarP(&abci, "abci", "", "socket", "Either socket or grpc")
  89. RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the command and results as if it were a console session")
  90. }
  91. func addQueryFlags() {
  92. queryCmd.PersistentFlags().StringVarP(&path, "path", "", "/store", "Path to prefix query with")
  93. queryCmd.PersistentFlags().IntVarP(&height, "height", "", 0, "Height to query the blockchain at")
  94. queryCmd.PersistentFlags().BoolVarP(&prove, "prove", "", false, "Whether or not to return a merkle proof of the query result")
  95. }
  96. func addCounterFlags() {
  97. counterCmd.PersistentFlags().StringVarP(&addrC, "addr", "", "tcp://0.0.0.0:46658", "Listen address")
  98. counterCmd.PersistentFlags().BoolVarP(&serial, "serial", "", false, "Enforce incrementing (serial) transactions")
  99. }
  100. func addDummyFlags() {
  101. dummyCmd.PersistentFlags().StringVarP(&addrD, "addr", "", "tcp://0.0.0.0:46658", "Listen address")
  102. dummyCmd.PersistentFlags().StringVarP(&persist, "persist", "", "", "Directory to use for a database")
  103. }
  104. func addCommands() {
  105. RootCmd.AddCommand(batchCmd)
  106. RootCmd.AddCommand(consoleCmd)
  107. RootCmd.AddCommand(echoCmd)
  108. RootCmd.AddCommand(infoCmd)
  109. RootCmd.AddCommand(setOptionCmd)
  110. RootCmd.AddCommand(deliverTxCmd)
  111. RootCmd.AddCommand(checkTxCmd)
  112. RootCmd.AddCommand(commitCmd)
  113. addQueryFlags()
  114. RootCmd.AddCommand(queryCmd)
  115. // examples
  116. addCounterFlags()
  117. RootCmd.AddCommand(counterCmd)
  118. addDummyFlags()
  119. RootCmd.AddCommand(dummyCmd)
  120. }
  121. var batchCmd = &cobra.Command{
  122. Use: "batch",
  123. Short: "Run a batch of abci commands against an application",
  124. Long: "",
  125. Args: cobra.ExactArgs(0),
  126. RunE: func(cmd *cobra.Command, args []string) error {
  127. return cmdBatch(cmd, args)
  128. },
  129. }
  130. var consoleCmd = &cobra.Command{
  131. Use: "console",
  132. Short: "Start an interactive abci console for multiple commands",
  133. Long: "",
  134. Args: cobra.ExactArgs(0),
  135. ValidArgs: []string{"batch", "echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"},
  136. RunE: func(cmd *cobra.Command, args []string) error {
  137. return cmdConsole(cmd, args)
  138. },
  139. }
  140. var echoCmd = &cobra.Command{
  141. Use: "echo",
  142. Short: "Have the application echo a message",
  143. Long: "",
  144. Args: cobra.ExactArgs(1),
  145. RunE: func(cmd *cobra.Command, args []string) error {
  146. return cmdEcho(cmd, args)
  147. },
  148. }
  149. var infoCmd = &cobra.Command{
  150. Use: "info",
  151. Short: "Get some info about the application",
  152. Long: "",
  153. Args: cobra.ExactArgs(0),
  154. RunE: func(cmd *cobra.Command, args []string) error {
  155. return cmdInfo(cmd, args)
  156. },
  157. }
  158. var setOptionCmd = &cobra.Command{
  159. Use: "set_option",
  160. Short: "Set an option on the application",
  161. Long: "",
  162. Args: cobra.ExactArgs(2),
  163. RunE: func(cmd *cobra.Command, args []string) error {
  164. return cmdSetOption(cmd, args)
  165. },
  166. }
  167. var deliverTxCmd = &cobra.Command{
  168. Use: "deliver_tx",
  169. Short: "Deliver a new transaction to the application",
  170. Long: "",
  171. Args: cobra.ExactArgs(1),
  172. RunE: func(cmd *cobra.Command, args []string) error {
  173. return cmdDeliverTx(cmd, args)
  174. },
  175. }
  176. var checkTxCmd = &cobra.Command{
  177. Use: "check_tx",
  178. Short: "Validate a transaction",
  179. Long: "",
  180. Args: cobra.ExactArgs(1),
  181. RunE: func(cmd *cobra.Command, args []string) error {
  182. return cmdCheckTx(cmd, args)
  183. },
  184. }
  185. var commitCmd = &cobra.Command{
  186. Use: "commit",
  187. Short: "Commit the application state and return the Merkle root hash",
  188. Long: "",
  189. Args: cobra.ExactArgs(0),
  190. RunE: func(cmd *cobra.Command, args []string) error {
  191. return cmdCommit(cmd, args)
  192. },
  193. }
  194. var queryCmd = &cobra.Command{
  195. Use: "query",
  196. Short: "Query the application state",
  197. Long: "",
  198. Args: cobra.ExactArgs(1),
  199. RunE: func(cmd *cobra.Command, args []string) error {
  200. return cmdQuery(cmd, args)
  201. },
  202. }
  203. var counterCmd = &cobra.Command{
  204. Use: "counter",
  205. Short: "ABCI demo example",
  206. Long: "",
  207. Args: cobra.ExactArgs(0),
  208. RunE: func(cmd *cobra.Command, args []string) error {
  209. return cmdCounter(cmd, args)
  210. },
  211. }
  212. var dummyCmd = &cobra.Command{
  213. Use: "dummy",
  214. Short: "ABCI demo example",
  215. Long: "",
  216. Args: cobra.ExactArgs(0),
  217. RunE: func(cmd *cobra.Command, args []string) error {
  218. return cmdDummy(cmd, args)
  219. },
  220. }
  221. // Generates new Args array based off of previous call args to maintain flag persistence
  222. func persistentArgs(line []byte) []string {
  223. // generate the arguments to run from original os.Args
  224. // to maintain flag arguments
  225. args := os.Args
  226. args = args[:len(args)-1] // remove the previous command argument
  227. if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors
  228. args = append(args, strings.Split(string(line), " ")...)
  229. }
  230. return args
  231. }
  232. //--------------------------------------------------------------------------------
  233. func cmdBatch(cmd *cobra.Command, args []string) error {
  234. bufReader := bufio.NewReader(os.Stdin)
  235. for {
  236. line, more, err := bufReader.ReadLine()
  237. if more {
  238. return errors.New("Input line is too long")
  239. } else if err == io.EOF {
  240. break
  241. } else if len(line) == 0 {
  242. continue
  243. } else if err != nil {
  244. return err
  245. }
  246. pArgs := persistentArgs(line)
  247. out, err := exec.Command(pArgs[0], pArgs[1:]...).Output()
  248. if err != nil {
  249. return err
  250. }
  251. fmt.Println(string(out))
  252. }
  253. return nil
  254. }
  255. func cmdConsole(cmd *cobra.Command, args []string) error {
  256. for {
  257. fmt.Printf("> ")
  258. bufReader := bufio.NewReader(os.Stdin)
  259. line, more, err := bufReader.ReadLine()
  260. if more {
  261. return errors.New("Input is too long")
  262. } else if err != nil {
  263. return err
  264. }
  265. pArgs := persistentArgs(line)
  266. out, err := exec.Command(pArgs[0], pArgs[1:]...).Output()
  267. if err != nil {
  268. return err
  269. }
  270. fmt.Println(string(out))
  271. }
  272. return nil
  273. }
  274. // Have the application echo a message
  275. func cmdEcho(cmd *cobra.Command, args []string) error {
  276. resEcho := client.EchoSync(args[0])
  277. printResponse(cmd, args, response{
  278. Data: resEcho.Data,
  279. })
  280. return nil
  281. }
  282. // Get some info from the application
  283. func cmdInfo(cmd *cobra.Command, args []string) error {
  284. var version string
  285. if len(args) == 1 {
  286. version = args[0]
  287. }
  288. resInfo, err := client.InfoSync(types.RequestInfo{version})
  289. if err != nil {
  290. return err
  291. }
  292. printResponse(cmd, args, response{
  293. Data: []byte(resInfo.Data),
  294. })
  295. return nil
  296. }
  297. // Set an option on the application
  298. func cmdSetOption(cmd *cobra.Command, args []string) error {
  299. resSetOption := client.SetOptionSync(args[0], args[1])
  300. printResponse(cmd, args, response{
  301. Log: resSetOption.Log,
  302. })
  303. return nil
  304. }
  305. // Append a new tx to application
  306. func cmdDeliverTx(cmd *cobra.Command, args []string) error {
  307. txBytes, err := stringOrHexToBytes(args[0])
  308. if err != nil {
  309. return err
  310. }
  311. res := client.DeliverTxSync(txBytes)
  312. printResponse(cmd, args, response{
  313. Code: res.Code,
  314. Data: res.Data,
  315. Log: res.Log,
  316. })
  317. return nil
  318. }
  319. // Validate a tx
  320. func cmdCheckTx(cmd *cobra.Command, args []string) error {
  321. txBytes, err := stringOrHexToBytes(args[0])
  322. if err != nil {
  323. return err
  324. }
  325. res := client.CheckTxSync(txBytes)
  326. printResponse(cmd, args, response{
  327. Code: res.Code,
  328. Data: res.Data,
  329. Log: res.Log,
  330. })
  331. return nil
  332. }
  333. // Get application Merkle root hash
  334. func cmdCommit(cmd *cobra.Command, args []string) error {
  335. res := client.CommitSync()
  336. printResponse(cmd, args, response{
  337. Code: res.Code,
  338. Data: res.Data,
  339. Log: res.Log,
  340. })
  341. return nil
  342. }
  343. // Query application state
  344. func cmdQuery(cmd *cobra.Command, args []string) error {
  345. queryBytes, err := stringOrHexToBytes(args[0])
  346. if err != nil {
  347. return err
  348. }
  349. resQuery, err := client.QuerySync(types.RequestQuery{
  350. Data: queryBytes,
  351. Path: path,
  352. Height: uint64(height),
  353. Prove: prove,
  354. })
  355. if err != nil {
  356. return err
  357. }
  358. printResponse(cmd, args, response{
  359. Code: resQuery.Code,
  360. Log: resQuery.Log,
  361. Query: &queryResponse{
  362. Key: resQuery.Key,
  363. Value: resQuery.Value,
  364. Height: resQuery.Height,
  365. Proof: resQuery.Proof,
  366. },
  367. })
  368. return nil
  369. }
  370. func cmdCounter(cmd *cobra.Command, args []string) error {
  371. app := counter.NewCounterApplication(serial)
  372. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  373. // Start the listener
  374. srv, err := server.NewServer(addrC, abci, app)
  375. if err != nil {
  376. return err
  377. }
  378. srv.SetLogger(logger.With("module", "abci-server"))
  379. if _, err := srv.Start(); err != nil {
  380. return err
  381. }
  382. // Wait forever
  383. cmn.TrapSignal(func() {
  384. // Cleanup
  385. srv.Stop()
  386. })
  387. return nil
  388. }
  389. func cmdDummy(cmd *cobra.Command, args []string) error {
  390. logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  391. // Create the application - in memory or persisted to disk
  392. var app types.Application
  393. if persist == "" {
  394. app = dummy.NewDummyApplication()
  395. } else {
  396. app = dummy.NewPersistentDummyApplication(persist)
  397. app.(*dummy.PersistentDummyApplication).SetLogger(logger.With("module", "dummy"))
  398. }
  399. // Start the listener
  400. srv, err := server.NewServer(addrD, abci, app)
  401. if err != nil {
  402. return err
  403. }
  404. srv.SetLogger(logger.With("module", "abci-server"))
  405. if _, err := srv.Start(); err != nil {
  406. return err
  407. }
  408. // Wait forever
  409. cmn.TrapSignal(func() {
  410. // Cleanup
  411. srv.Stop()
  412. })
  413. return nil
  414. }
  415. //--------------------------------------------------------------------------------
  416. func printResponse(cmd *cobra.Command, args []string, rsp response) {
  417. if verbose {
  418. fmt.Println(">", cmd.Use, strings.Join(args, " "))
  419. }
  420. // Always print the status code.
  421. fmt.Printf("-> code: %s\n", rsp.Code.String())
  422. if len(rsp.Data) != 0 {
  423. // Do no print this line when using the commit command
  424. // because the string comes out as gibberish
  425. if cmd.Use != "commit" {
  426. fmt.Printf("-> data: %s\n", rsp.Data)
  427. }
  428. fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
  429. }
  430. if rsp.Log != "" {
  431. fmt.Printf("-> log: %s\n", rsp.Log)
  432. }
  433. if rsp.Query != nil {
  434. fmt.Printf("-> height: %d\n", rsp.Query.Height)
  435. if rsp.Query.Key != nil {
  436. fmt.Printf("-> key: %s\n", rsp.Query.Key)
  437. fmt.Printf("-> key.hex: %X\n", rsp.Query.Key)
  438. }
  439. if rsp.Query.Value != nil {
  440. fmt.Printf("-> value: %s\n", rsp.Query.Value)
  441. fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
  442. }
  443. if rsp.Query.Proof != nil {
  444. fmt.Printf("-> proof: %X\n", rsp.Query.Proof)
  445. }
  446. }
  447. }
  448. // NOTE: s is interpreted as a string unless prefixed with 0x
  449. func stringOrHexToBytes(s string) ([]byte, error) {
  450. if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
  451. b, err := hex.DecodeString(s[2:])
  452. if err != nil {
  453. err = fmt.Errorf("Error decoding hex argument: %s", err.Error())
  454. return nil, err
  455. }
  456. return b, nil
  457. }
  458. if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") {
  459. err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s)
  460. return nil, err
  461. }
  462. return []byte(s[1 : len(s)-1]), nil
  463. }