github.com/Team-Kujira/tendermint@v0.34.24-indexer/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  }