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  }