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