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