github.com/Oyster-zx/tendermint@v0.34.24-fork/abci/cmd/abci-cli/abci-cli.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "io" 9 "os" 10 "strings" 11 12 "github.com/spf13/cobra" 13 14 "github.com/tendermint/tendermint/libs/log" 15 tmos "github.com/tendermint/tendermint/libs/os" 16 17 abcicli "github.com/tendermint/tendermint/abci/client" 18 "github.com/tendermint/tendermint/abci/example/code" 19 "github.com/tendermint/tendermint/abci/example/counter" 20 "github.com/tendermint/tendermint/abci/example/kvstore" 21 "github.com/tendermint/tendermint/abci/server" 22 servertest "github.com/tendermint/tendermint/abci/tests/server" 23 "github.com/tendermint/tendermint/abci/types" 24 "github.com/tendermint/tendermint/abci/version" 25 "github.com/tendermint/tendermint/proto/tendermint/crypto" 26 ) 27 28 // client is a global variable so it can be reused by the console 29 var ( 30 client abcicli.Client 31 logger log.Logger 32 ) 33 34 // flags 35 var ( 36 // global 37 flagAddress string 38 flagAbci string 39 flagVerbose bool // for the println output 40 flagLogLevel string // for the logger 41 42 // query 43 flagPath string 44 flagHeight int 45 flagProve bool 46 47 // counter 48 flagSerial bool 49 50 // kvstore 51 flagPersist string 52 ) 53 54 var RootCmd = &cobra.Command{ 55 Use: "abci-cli", 56 Short: "the ABCI CLI tool wraps an ABCI client", 57 Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers", 58 PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 59 60 switch cmd.Use { 61 case "counter", "kvstore": // for the examples apps, don't pre-run 62 return nil 63 case "version": // skip running for version command 64 return nil 65 } 66 67 if logger == nil { 68 allowLevel, err := log.AllowLevel(flagLogLevel) 69 if err != nil { 70 return err 71 } 72 logger = log.NewFilter(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), allowLevel) 73 } 74 if client == nil { 75 var err error 76 client, err = abcicli.NewClient(flagAddress, flagAbci, false) 77 if err != nil { 78 return err 79 } 80 client.SetLogger(logger.With("module", "abci-client")) 81 if err := client.Start(); err != nil { 82 return err 83 } 84 } 85 return nil 86 }, 87 } 88 89 // Structure for data passed to print response. 90 type response struct { 91 // generic abci response 92 Data []byte 93 Code uint32 94 Info string 95 Log string 96 97 Query *queryResponse 98 } 99 100 type queryResponse struct { 101 Key []byte 102 Value []byte 103 Height int64 104 ProofOps *crypto.ProofOps 105 } 106 107 func Execute() error { 108 addGlobalFlags() 109 addCommands() 110 return RootCmd.Execute() 111 } 112 113 func addGlobalFlags() { 114 RootCmd.PersistentFlags().StringVarP(&flagAddress, 115 "address", 116 "", 117 "tcp://0.0.0.0:26658", 118 "address of application socket") 119 RootCmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc") 120 RootCmd.PersistentFlags().BoolVarP(&flagVerbose, 121 "verbose", 122 "v", 123 false, 124 "print the command and results as if it were a console session") 125 RootCmd.PersistentFlags().StringVarP(&flagLogLevel, "log_level", "", "debug", "set the logger level") 126 } 127 128 func addQueryFlags() { 129 queryCmd.PersistentFlags().StringVarP(&flagPath, "path", "", "/store", "path to prefix query with") 130 queryCmd.PersistentFlags().IntVarP(&flagHeight, "height", "", 0, "height to query the blockchain at") 131 queryCmd.PersistentFlags().BoolVarP(&flagProve, 132 "prove", 133 "", 134 false, 135 "whether or not to return a merkle proof of the query result") 136 } 137 138 func addCounterFlags() { 139 counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions") 140 } 141 142 func addKVStoreFlags() { 143 kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") 144 } 145 146 func addCommands() { 147 RootCmd.AddCommand(batchCmd) 148 RootCmd.AddCommand(consoleCmd) 149 RootCmd.AddCommand(echoCmd) 150 RootCmd.AddCommand(infoCmd) 151 RootCmd.AddCommand(setOptionCmd) 152 RootCmd.AddCommand(deliverTxCmd) 153 RootCmd.AddCommand(checkTxCmd) 154 RootCmd.AddCommand(commitCmd) 155 RootCmd.AddCommand(versionCmd) 156 RootCmd.AddCommand(testCmd) 157 addQueryFlags() 158 RootCmd.AddCommand(queryCmd) 159 160 // examples 161 addCounterFlags() 162 RootCmd.AddCommand(counterCmd) 163 addKVStoreFlags() 164 RootCmd.AddCommand(kvstoreCmd) 165 } 166 167 var batchCmd = &cobra.Command{ 168 Use: "batch", 169 Short: "run a batch of abci commands against an application", 170 Long: `run a batch of abci commands against an application 171 172 This command is run by piping in a file containing a series of commands 173 you'd like to run: 174 175 abci-cli batch < example.file 176 177 where example.file looks something like: 178 179 set_option serial on 180 check_tx 0x00 181 check_tx 0xff 182 deliver_tx 0x00 183 check_tx 0x00 184 deliver_tx 0x01 185 deliver_tx 0x04 186 info 187 `, 188 Args: cobra.ExactArgs(0), 189 RunE: cmdBatch, 190 } 191 192 var consoleCmd = &cobra.Command{ 193 Use: "console", 194 Short: "start an interactive ABCI console for multiple commands", 195 Long: `start an interactive ABCI console for multiple commands 196 197 This command opens an interactive console for running any of the other commands 198 without opening a new connection each time 199 `, 200 Args: cobra.ExactArgs(0), 201 ValidArgs: []string{"echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"}, 202 RunE: cmdConsole, 203 } 204 205 var echoCmd = &cobra.Command{ 206 Use: "echo", 207 Short: "have the application echo a message", 208 Long: "have the application echo a message", 209 Args: cobra.ExactArgs(1), 210 RunE: cmdEcho, 211 } 212 var infoCmd = &cobra.Command{ 213 Use: "info", 214 Short: "get some info about the application", 215 Long: "get some info about the application", 216 Args: cobra.ExactArgs(0), 217 RunE: cmdInfo, 218 } 219 var setOptionCmd = &cobra.Command{ 220 Use: "set_option", 221 Short: "set an option on the application", 222 Long: "set an option on the application", 223 Args: cobra.ExactArgs(2), 224 RunE: cmdSetOption, 225 } 226 227 var deliverTxCmd = &cobra.Command{ 228 Use: "deliver_tx", 229 Short: "deliver a new transaction to the application", 230 Long: "deliver a new transaction to the application", 231 Args: cobra.ExactArgs(1), 232 RunE: cmdDeliverTx, 233 } 234 235 var checkTxCmd = &cobra.Command{ 236 Use: "check_tx", 237 Short: "validate a transaction", 238 Long: "validate a transaction", 239 Args: cobra.ExactArgs(1), 240 RunE: cmdCheckTx, 241 } 242 243 var commitCmd = &cobra.Command{ 244 Use: "commit", 245 Short: "commit the application state and return the Merkle root hash", 246 Long: "commit the application state and return the Merkle root hash", 247 Args: cobra.ExactArgs(0), 248 RunE: cmdCommit, 249 } 250 251 var versionCmd = &cobra.Command{ 252 Use: "version", 253 Short: "print ABCI console version", 254 Long: "print ABCI console version", 255 Args: cobra.ExactArgs(0), 256 RunE: func(cmd *cobra.Command, args []string) error { 257 fmt.Println(version.Version) 258 return nil 259 }, 260 } 261 262 var queryCmd = &cobra.Command{ 263 Use: "query", 264 Short: "query the application state", 265 Long: "query the application state", 266 Args: cobra.ExactArgs(1), 267 RunE: cmdQuery, 268 } 269 270 var counterCmd = &cobra.Command{ 271 Use: "counter", 272 Short: "ABCI demo example", 273 Long: "ABCI demo example", 274 Args: cobra.ExactArgs(0), 275 RunE: cmdCounter, 276 } 277 278 var kvstoreCmd = &cobra.Command{ 279 Use: "kvstore", 280 Short: "ABCI demo example", 281 Long: "ABCI demo example", 282 Args: cobra.ExactArgs(0), 283 RunE: cmdKVStore, 284 } 285 286 var testCmd = &cobra.Command{ 287 Use: "test", 288 Short: "run integration tests", 289 Long: "run integration tests", 290 Args: cobra.ExactArgs(0), 291 RunE: cmdTest, 292 } 293 294 // Generates new Args array based off of previous call args to maintain flag persistence 295 func persistentArgs(line []byte) []string { 296 297 // generate the arguments to run from original os.Args 298 // to maintain flag arguments 299 args := os.Args 300 args = args[:len(args)-1] // remove the previous command argument 301 302 if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors 303 args = append(args, strings.Split(string(line), " ")...) 304 } 305 return args 306 } 307 308 //-------------------------------------------------------------------------------- 309 310 func compose(fs []func() error) error { 311 if len(fs) == 0 { 312 return nil 313 } 314 315 err := fs[0]() 316 if err == nil { 317 return compose(fs[1:]) 318 } 319 320 return err 321 } 322 323 func cmdTest(cmd *cobra.Command, args []string) error { 324 return compose( 325 []func() error{ 326 func() error { return servertest.InitChain(client) }, 327 func() error { return servertest.SetOption(client, "serial", "on") }, 328 func() error { return servertest.Commit(client, nil) }, 329 func() error { return servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) }, 330 func() error { return servertest.Commit(client, nil) }, 331 func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil) }, 332 func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) }, 333 func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) }, 334 func() error { return servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil) }, 335 func() error { return servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) }, 336 func() error { return servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) }, 337 func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) }, 338 func() error { 339 return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil) 340 }, 341 func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) }, 342 }) 343 } 344 345 func cmdBatch(cmd *cobra.Command, args []string) error { 346 bufReader := bufio.NewReader(os.Stdin) 347 LOOP: 348 for { 349 350 line, more, err := bufReader.ReadLine() 351 switch { 352 case more: 353 return errors.New("input line is too long") 354 case err == io.EOF: 355 break LOOP 356 case len(line) == 0: 357 continue 358 case err != nil: 359 return err 360 } 361 362 cmdArgs := persistentArgs(line) 363 if err := muxOnCommands(cmd, cmdArgs); err != nil { 364 return err 365 } 366 fmt.Println() 367 } 368 return nil 369 } 370 371 func cmdConsole(cmd *cobra.Command, args []string) error { 372 for { 373 fmt.Printf("> ") 374 bufReader := bufio.NewReader(os.Stdin) 375 line, more, err := bufReader.ReadLine() 376 if more { 377 return errors.New("input is too long") 378 } else if err != nil { 379 return err 380 } 381 382 pArgs := persistentArgs(line) 383 if err := muxOnCommands(cmd, pArgs); err != nil { 384 return err 385 } 386 } 387 } 388 389 func muxOnCommands(cmd *cobra.Command, pArgs []string) error { 390 if len(pArgs) < 2 { 391 return errors.New("expecting persistent args of the form: abci-cli [command] <...>") 392 } 393 394 // TODO: this parsing is fragile 395 args := []string{} 396 for i := 0; i < len(pArgs); i++ { 397 arg := pArgs[i] 398 399 // check for flags 400 if strings.HasPrefix(arg, "-") { 401 // if it has an equal, we can just skip 402 if strings.Contains(arg, "=") { 403 continue 404 } 405 // if its a boolean, we can just skip 406 _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-")) 407 if err == nil { 408 continue 409 } 410 411 // otherwise, we need to skip the next one too 412 i++ 413 continue 414 } 415 416 // append the actual arg 417 args = append(args, arg) 418 } 419 var subCommand string 420 var actualArgs []string 421 if len(args) > 1 { 422 subCommand = args[1] 423 } 424 if len(args) > 2 { 425 actualArgs = args[2:] 426 } 427 cmd.Use = subCommand // for later print statements ... 428 429 switch strings.ToLower(subCommand) { 430 case "check_tx": 431 return cmdCheckTx(cmd, actualArgs) 432 case "commit": 433 return cmdCommit(cmd, actualArgs) 434 case "deliver_tx": 435 return cmdDeliverTx(cmd, actualArgs) 436 case "echo": 437 return cmdEcho(cmd, actualArgs) 438 case "info": 439 return cmdInfo(cmd, actualArgs) 440 case "query": 441 return cmdQuery(cmd, actualArgs) 442 case "set_option": 443 return cmdSetOption(cmd, actualArgs) 444 default: 445 return cmdUnimplemented(cmd, pArgs) 446 } 447 } 448 449 func cmdUnimplemented(cmd *cobra.Command, args []string) error { 450 msg := "unimplemented command" 451 452 if len(args) > 0 { 453 msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " ")) 454 } 455 printResponse(cmd, args, response{ 456 Code: codeBad, 457 Log: msg, 458 }) 459 460 fmt.Println("Available commands:") 461 fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short) 462 fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short) 463 fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short) 464 fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short) 465 fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short) 466 fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short) 467 fmt.Printf("%s: %s\n", setOptionCmd.Use, setOptionCmd.Short) 468 fmt.Println("Use \"[command] --help\" for more information about a command.") 469 470 return nil 471 } 472 473 // Have the application echo a message 474 func cmdEcho(cmd *cobra.Command, args []string) error { 475 msg := "" 476 if len(args) > 0 { 477 msg = args[0] 478 } 479 res, err := client.EchoSync(msg) 480 if err != nil { 481 return err 482 } 483 printResponse(cmd, args, response{ 484 Data: []byte(res.Message), 485 }) 486 return nil 487 } 488 489 // Get some info from the application 490 func cmdInfo(cmd *cobra.Command, args []string) error { 491 var version string 492 if len(args) == 1 { 493 version = args[0] 494 } 495 res, err := client.InfoSync(types.RequestInfo{Version: version}) 496 if err != nil { 497 return err 498 } 499 printResponse(cmd, args, response{ 500 Data: []byte(res.Data), 501 }) 502 return nil 503 } 504 505 const codeBad uint32 = 10 506 507 // Set an option on the application 508 func cmdSetOption(cmd *cobra.Command, args []string) error { 509 if len(args) < 2 { 510 printResponse(cmd, args, response{ 511 Code: codeBad, 512 Log: "want at least arguments of the form: <key> <value>", 513 }) 514 return nil 515 } 516 517 key, val := args[0], args[1] 518 _, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: val}) 519 if err != nil { 520 return err 521 } 522 printResponse(cmd, args, response{Log: "OK (SetOption doesn't return anything.)"}) // NOTE: Nothing to show... 523 return nil 524 } 525 526 // Append a new tx to application 527 func cmdDeliverTx(cmd *cobra.Command, args []string) error { 528 if len(args) == 0 { 529 printResponse(cmd, args, response{ 530 Code: codeBad, 531 Log: "want the tx", 532 }) 533 return nil 534 } 535 txBytes, err := stringOrHexToBytes(args[0]) 536 if err != nil { 537 return err 538 } 539 res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes}) 540 if err != nil { 541 return err 542 } 543 printResponse(cmd, args, response{ 544 Code: res.Code, 545 Data: res.Data, 546 Info: res.Info, 547 Log: res.Log, 548 }) 549 return nil 550 } 551 552 // Validate a tx 553 func cmdCheckTx(cmd *cobra.Command, args []string) error { 554 if len(args) == 0 { 555 printResponse(cmd, args, response{ 556 Code: codeBad, 557 Info: "want the tx", 558 }) 559 return nil 560 } 561 txBytes, err := stringOrHexToBytes(args[0]) 562 if err != nil { 563 return err 564 } 565 res, err := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes}) 566 if err != nil { 567 return err 568 } 569 printResponse(cmd, args, response{ 570 Code: res.Code, 571 Data: res.Data, 572 Info: res.Info, 573 Log: res.Log, 574 }) 575 return nil 576 } 577 578 // Get application Merkle root hash 579 func cmdCommit(cmd *cobra.Command, args []string) error { 580 res, err := client.CommitSync() 581 if err != nil { 582 return err 583 } 584 printResponse(cmd, args, response{ 585 Data: res.Data, 586 }) 587 return nil 588 } 589 590 // Query application state 591 func cmdQuery(cmd *cobra.Command, args []string) error { 592 if len(args) == 0 { 593 printResponse(cmd, args, response{ 594 Code: codeBad, 595 Info: "want the query", 596 Log: "", 597 }) 598 return nil 599 } 600 queryBytes, err := stringOrHexToBytes(args[0]) 601 if err != nil { 602 return err 603 } 604 605 resQuery, err := client.QuerySync(types.RequestQuery{ 606 Data: queryBytes, 607 Path: flagPath, 608 Height: int64(flagHeight), 609 Prove: flagProve, 610 }) 611 if err != nil { 612 return err 613 } 614 printResponse(cmd, args, response{ 615 Code: resQuery.Code, 616 Info: resQuery.Info, 617 Log: resQuery.Log, 618 Query: &queryResponse{ 619 Key: resQuery.Key, 620 Value: resQuery.Value, 621 Height: resQuery.Height, 622 ProofOps: resQuery.ProofOps, 623 }, 624 }) 625 return nil 626 } 627 628 func cmdCounter(cmd *cobra.Command, args []string) error { 629 app := counter.NewApplication(flagSerial) 630 logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 631 632 // Start the listener 633 srv, err := server.NewServer(flagAddress, flagAbci, app) 634 if err != nil { 635 return err 636 } 637 srv.SetLogger(logger.With("module", "abci-server")) 638 if err := srv.Start(); err != nil { 639 return err 640 } 641 642 // Stop upon receiving SIGTERM or CTRL-C. 643 tmos.TrapSignal(logger, func() { 644 // Cleanup 645 if err := srv.Stop(); err != nil { 646 logger.Error("Error while stopping server", "err", err) 647 } 648 }) 649 650 // Run forever. 651 select {} 652 } 653 654 func cmdKVStore(cmd *cobra.Command, args []string) error { 655 logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 656 657 // Create the application - in memory or persisted to disk 658 var app types.Application 659 if flagPersist == "" { 660 app = kvstore.NewApplication() 661 } else { 662 app = kvstore.NewPersistentKVStoreApplication(flagPersist) 663 app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore")) 664 } 665 666 // Start the listener 667 srv, err := server.NewServer(flagAddress, flagAbci, app) 668 if err != nil { 669 return err 670 } 671 srv.SetLogger(logger.With("module", "abci-server")) 672 if err := srv.Start(); err != nil { 673 return err 674 } 675 676 // Stop upon receiving SIGTERM or CTRL-C. 677 tmos.TrapSignal(logger, func() { 678 // Cleanup 679 if err := srv.Stop(); err != nil { 680 logger.Error("Error while stopping server", "err", err) 681 } 682 }) 683 684 // Run forever. 685 select {} 686 } 687 688 //-------------------------------------------------------------------------------- 689 690 func printResponse(cmd *cobra.Command, args []string, rsp response) { 691 692 if flagVerbose { 693 fmt.Println(">", cmd.Use, strings.Join(args, " ")) 694 } 695 696 // Always print the status code. 697 if rsp.Code == types.CodeTypeOK { 698 fmt.Printf("-> code: OK\n") 699 } else { 700 fmt.Printf("-> code: %d\n", rsp.Code) 701 702 } 703 704 if len(rsp.Data) != 0 { 705 // Do no print this line when using the commit command 706 // because the string comes out as gibberish 707 if cmd.Use != "commit" { 708 fmt.Printf("-> data: %s\n", rsp.Data) 709 } 710 fmt.Printf("-> data.hex: 0x%X\n", rsp.Data) 711 } 712 if rsp.Log != "" { 713 fmt.Printf("-> log: %s\n", rsp.Log) 714 } 715 716 if rsp.Query != nil { 717 fmt.Printf("-> height: %d\n", rsp.Query.Height) 718 if rsp.Query.Key != nil { 719 fmt.Printf("-> key: %s\n", rsp.Query.Key) 720 fmt.Printf("-> key.hex: %X\n", rsp.Query.Key) 721 } 722 if rsp.Query.Value != nil { 723 fmt.Printf("-> value: %s\n", rsp.Query.Value) 724 fmt.Printf("-> value.hex: %X\n", rsp.Query.Value) 725 } 726 if rsp.Query.ProofOps != nil { 727 fmt.Printf("-> proof: %#v\n", rsp.Query.ProofOps) 728 } 729 } 730 } 731 732 // NOTE: s is interpreted as a string unless prefixed with 0x 733 func stringOrHexToBytes(s string) ([]byte, error) { 734 if len(s) > 2 && strings.ToLower(s[:2]) == "0x" { 735 b, err := hex.DecodeString(s[2:]) 736 if err != nil { 737 err = fmt.Errorf("error decoding hex argument: %s", err.Error()) 738 return nil, err 739 } 740 return b, nil 741 } 742 743 if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") { 744 err := fmt.Errorf("invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s) 745 return nil, err 746 } 747 748 return []byte(s[1 : len(s)-1]), nil 749 }