github.com/cosmos/cosmos-sdk@v0.50.10/client/context.go (about)

     1  package client
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  
    11  	"github.com/cosmos/gogoproto/proto"
    12  	"github.com/spf13/viper"
    13  	"google.golang.org/grpc"
    14  	"sigs.k8s.io/yaml"
    15  
    16  	"github.com/cosmos/cosmos-sdk/codec"
    17  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    18  	"github.com/cosmos/cosmos-sdk/crypto/keyring"
    19  	sdk "github.com/cosmos/cosmos-sdk/types"
    20  )
    21  
    22  // PreprocessTxFn defines a hook by which chains can preprocess transactions before broadcasting
    23  type PreprocessTxFn func(chainID string, key keyring.KeyType, tx TxBuilder) error
    24  
    25  // Context implements a typical context created in SDK modules for transaction
    26  // handling and queries.
    27  type Context struct {
    28  	FromAddress       sdk.AccAddress
    29  	Client            CometRPC
    30  	GRPCClient        *grpc.ClientConn
    31  	ChainID           string
    32  	Codec             codec.Codec
    33  	InterfaceRegistry codectypes.InterfaceRegistry
    34  	Input             io.Reader
    35  	Keyring           keyring.Keyring
    36  	KeyringOptions    []keyring.Option
    37  	Output            io.Writer
    38  	OutputFormat      string
    39  	Height            int64
    40  	HomeDir           string
    41  	KeyringDir        string
    42  	From              string
    43  	BroadcastMode     string
    44  	FromName          string
    45  	SignModeStr       string
    46  	UseLedger         bool
    47  	Simulate          bool
    48  	GenerateOnly      bool
    49  	Offline           bool
    50  	SkipConfirm       bool
    51  	TxConfig          TxConfig
    52  	AccountRetriever  AccountRetriever
    53  	NodeURI           string
    54  	FeePayer          sdk.AccAddress
    55  	FeeGranter        sdk.AccAddress
    56  	Viper             *viper.Viper
    57  	LedgerHasProtobuf bool
    58  	PreprocessTxHook  PreprocessTxFn
    59  
    60  	// IsAux is true when the signer is an auxiliary signer (e.g. the tipper).
    61  	IsAux bool
    62  
    63  	// TODO: Deprecated (remove).
    64  	LegacyAmino *codec.LegacyAmino
    65  
    66  	// CmdContext is the context.Context from the Cobra command.
    67  	CmdContext context.Context
    68  }
    69  
    70  // WithCmdContext returns a copy of the context with an updated context.Context,
    71  // usually set to the cobra cmd context.
    72  func (ctx Context) WithCmdContext(c context.Context) Context {
    73  	ctx.CmdContext = c
    74  	return ctx
    75  }
    76  
    77  // WithKeyring returns a copy of the context with an updated keyring.
    78  func (ctx Context) WithKeyring(k keyring.Keyring) Context {
    79  	ctx.Keyring = k
    80  	return ctx
    81  }
    82  
    83  // WithKeyringOptions returns a copy of the context with an updated keyring.
    84  func (ctx Context) WithKeyringOptions(opts ...keyring.Option) Context {
    85  	ctx.KeyringOptions = opts
    86  	return ctx
    87  }
    88  
    89  // WithInput returns a copy of the context with an updated input.
    90  func (ctx Context) WithInput(r io.Reader) Context {
    91  	// convert to a bufio.Reader to have a shared buffer between the keyring and the
    92  	// the Commands, ensuring a read from one advance the read pointer for the other.
    93  	// see https://github.com/cosmos/cosmos-sdk/issues/9566.
    94  	ctx.Input = bufio.NewReader(r)
    95  	return ctx
    96  }
    97  
    98  // WithCodec returns a copy of the Context with an updated Codec.
    99  func (ctx Context) WithCodec(m codec.Codec) Context {
   100  	ctx.Codec = m
   101  	return ctx
   102  }
   103  
   104  // WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec.
   105  // TODO: Deprecated (remove).
   106  func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context {
   107  	ctx.LegacyAmino = cdc
   108  	return ctx
   109  }
   110  
   111  // WithOutput returns a copy of the context with an updated output writer (e.g. stdout).
   112  func (ctx Context) WithOutput(w io.Writer) Context {
   113  	ctx.Output = w
   114  	return ctx
   115  }
   116  
   117  // WithFrom returns a copy of the context with an updated from address or name.
   118  func (ctx Context) WithFrom(from string) Context {
   119  	ctx.From = from
   120  	return ctx
   121  }
   122  
   123  // WithOutputFormat returns a copy of the context with an updated OutputFormat field.
   124  func (ctx Context) WithOutputFormat(format string) Context {
   125  	ctx.OutputFormat = format
   126  	return ctx
   127  }
   128  
   129  // WithNodeURI returns a copy of the context with an updated node URI.
   130  func (ctx Context) WithNodeURI(nodeURI string) Context {
   131  	ctx.NodeURI = nodeURI
   132  	return ctx
   133  }
   134  
   135  // WithHeight returns a copy of the context with an updated height.
   136  func (ctx Context) WithHeight(height int64) Context {
   137  	ctx.Height = height
   138  	return ctx
   139  }
   140  
   141  // WithClient returns a copy of the context with an updated RPC client
   142  // instance.
   143  func (ctx Context) WithClient(client CometRPC) Context {
   144  	ctx.Client = client
   145  	return ctx
   146  }
   147  
   148  // WithGRPCClient returns a copy of the context with an updated GRPC client
   149  // instance.
   150  func (ctx Context) WithGRPCClient(grpcClient *grpc.ClientConn) Context {
   151  	ctx.GRPCClient = grpcClient
   152  	return ctx
   153  }
   154  
   155  // WithUseLedger returns a copy of the context with an updated UseLedger flag.
   156  func (ctx Context) WithUseLedger(useLedger bool) Context {
   157  	ctx.UseLedger = useLedger
   158  	return ctx
   159  }
   160  
   161  // WithChainID returns a copy of the context with an updated chain ID.
   162  func (ctx Context) WithChainID(chainID string) Context {
   163  	ctx.ChainID = chainID
   164  	return ctx
   165  }
   166  
   167  // WithHomeDir returns a copy of the Context with HomeDir set.
   168  func (ctx Context) WithHomeDir(dir string) Context {
   169  	if dir != "" {
   170  		ctx.HomeDir = dir
   171  	}
   172  	return ctx
   173  }
   174  
   175  // WithKeyringDir returns a copy of the Context with KeyringDir set.
   176  func (ctx Context) WithKeyringDir(dir string) Context {
   177  	ctx.KeyringDir = dir
   178  	return ctx
   179  }
   180  
   181  // WithGenerateOnly returns a copy of the context with updated GenerateOnly value
   182  func (ctx Context) WithGenerateOnly(generateOnly bool) Context {
   183  	ctx.GenerateOnly = generateOnly
   184  	return ctx
   185  }
   186  
   187  // WithSimulation returns a copy of the context with updated Simulate value
   188  func (ctx Context) WithSimulation(simulate bool) Context {
   189  	ctx.Simulate = simulate
   190  	return ctx
   191  }
   192  
   193  // WithOffline returns a copy of the context with updated Offline value.
   194  func (ctx Context) WithOffline(offline bool) Context {
   195  	ctx.Offline = offline
   196  	return ctx
   197  }
   198  
   199  // WithFromName returns a copy of the context with an updated from account name.
   200  func (ctx Context) WithFromName(name string) Context {
   201  	ctx.FromName = name
   202  	return ctx
   203  }
   204  
   205  // WithFromAddress returns a copy of the context with an updated from account
   206  // address.
   207  func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context {
   208  	ctx.FromAddress = addr
   209  	return ctx
   210  }
   211  
   212  // WithFeePayerAddress returns a copy of the context with an updated fee payer account
   213  // address.
   214  func (ctx Context) WithFeePayerAddress(addr sdk.AccAddress) Context {
   215  	ctx.FeePayer = addr
   216  	return ctx
   217  }
   218  
   219  // WithFeeGranterAddress returns a copy of the context with an updated fee granter account
   220  // address.
   221  func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context {
   222  	ctx.FeeGranter = addr
   223  	return ctx
   224  }
   225  
   226  // WithBroadcastMode returns a copy of the context with an updated broadcast
   227  // mode.
   228  func (ctx Context) WithBroadcastMode(mode string) Context {
   229  	ctx.BroadcastMode = mode
   230  	return ctx
   231  }
   232  
   233  // WithSignModeStr returns a copy of the context with an updated SignMode
   234  // value.
   235  func (ctx Context) WithSignModeStr(signModeStr string) Context {
   236  	ctx.SignModeStr = signModeStr
   237  	return ctx
   238  }
   239  
   240  // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm
   241  // value.
   242  func (ctx Context) WithSkipConfirmation(skip bool) Context {
   243  	ctx.SkipConfirm = skip
   244  	return ctx
   245  }
   246  
   247  // WithTxConfig returns the context with an updated TxConfig
   248  func (ctx Context) WithTxConfig(generator TxConfig) Context {
   249  	ctx.TxConfig = generator
   250  	return ctx
   251  }
   252  
   253  // WithAccountRetriever returns the context with an updated AccountRetriever
   254  func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context {
   255  	ctx.AccountRetriever = retriever
   256  	return ctx
   257  }
   258  
   259  // WithInterfaceRegistry returns the context with an updated InterfaceRegistry
   260  func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context {
   261  	ctx.InterfaceRegistry = interfaceRegistry
   262  	return ctx
   263  }
   264  
   265  // WithViper returns the context with Viper field. This Viper instance is used to read
   266  // client-side config from the config file.
   267  func (ctx Context) WithViper(prefix string) Context {
   268  	v := viper.New()
   269  	v.SetEnvPrefix(prefix)
   270  	v.AutomaticEnv()
   271  	ctx.Viper = v
   272  	return ctx
   273  }
   274  
   275  // WithAux returns a copy of the context with an updated IsAux value.
   276  func (ctx Context) WithAux(isAux bool) Context {
   277  	ctx.IsAux = isAux
   278  	return ctx
   279  }
   280  
   281  // WithLedgerHasProto returns the context with the provided boolean value, indicating
   282  // whether the target Ledger application can support Protobuf payloads.
   283  func (ctx Context) WithLedgerHasProtobuf(val bool) Context {
   284  	ctx.LedgerHasProtobuf = val
   285  	return ctx
   286  }
   287  
   288  // WithPreprocessTxHook returns the context with the provided preprocessing hook, which
   289  // enables chains to preprocess the transaction using the builder.
   290  func (ctx Context) WithPreprocessTxHook(preprocessFn PreprocessTxFn) Context {
   291  	ctx.PreprocessTxHook = preprocessFn
   292  	return ctx
   293  }
   294  
   295  // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout
   296  func (ctx Context) PrintString(str string) error {
   297  	return ctx.PrintBytes([]byte(str))
   298  }
   299  
   300  // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout.
   301  // NOTE: for printing a complex state object, you should use ctx.PrintOutput
   302  func (ctx Context) PrintBytes(o []byte) error {
   303  	writer := ctx.Output
   304  	if writer == nil {
   305  		writer = os.Stdout
   306  	}
   307  
   308  	_, err := writer.Write(o)
   309  	return err
   310  }
   311  
   312  // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is
   313  // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint
   314  // will be JSON encoded using ctx.Codec. An error is returned upon failure.
   315  func (ctx Context) PrintProto(toPrint proto.Message) error {
   316  	// always serialize JSON initially because proto json can't be directly YAML encoded
   317  	out, err := ctx.Codec.MarshalJSON(toPrint)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	return ctx.printOutput(out)
   322  }
   323  
   324  // PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type
   325  // and uses amino JSON encoding.
   326  // Deprecated: It will be removed in the near future!
   327  func (ctx Context) PrintObjectLegacy(toPrint interface{}) error {
   328  	out, err := ctx.LegacyAmino.MarshalJSON(toPrint)
   329  	if err != nil {
   330  		return err
   331  	}
   332  	return ctx.printOutput(out)
   333  }
   334  
   335  // PrintRaw is a variant of PrintProto that doesn't require a proto.Message type
   336  // and uses a raw JSON message. No marshaling is performed.
   337  func (ctx Context) PrintRaw(toPrint json.RawMessage) error {
   338  	return ctx.printOutput(toPrint)
   339  }
   340  
   341  func (ctx Context) printOutput(out []byte) error {
   342  	var err error
   343  	if ctx.OutputFormat == "text" {
   344  		out, err = yaml.JSONToYAML(out)
   345  		if err != nil {
   346  			return err
   347  		}
   348  	}
   349  
   350  	writer := ctx.Output
   351  	if writer == nil {
   352  		writer = os.Stdout
   353  	}
   354  
   355  	_, err = writer.Write(out)
   356  	if err != nil {
   357  		return err
   358  	}
   359  
   360  	if ctx.OutputFormat != "text" {
   361  		// append new-line for formats besides YAML
   362  		_, err = writer.Write([]byte("\n"))
   363  		if err != nil {
   364  			return err
   365  		}
   366  	}
   367  
   368  	return nil
   369  }
   370  
   371  // GetFromFields returns a from account address, account name and keyring type, given either an address or key name.
   372  // If clientCtx.Simulate is true the keystore is not accessed and a valid address must be provided
   373  // If clientCtx.GenerateOnly is true the keystore is only accessed if a key name is provided
   374  func GetFromFields(clientCtx Context, kr keyring.Keyring, from string) (sdk.AccAddress, string, keyring.KeyType, error) {
   375  	if from == "" {
   376  		return nil, "", 0, nil
   377  	}
   378  
   379  	addr, err := sdk.AccAddressFromBech32(from)
   380  	switch {
   381  	case clientCtx.Simulate:
   382  		if err != nil {
   383  			return nil, "", 0, fmt.Errorf("a valid bech32 address must be provided in simulation mode: %w", err)
   384  		}
   385  
   386  		return addr, "", 0, nil
   387  
   388  	case clientCtx.GenerateOnly:
   389  		if err == nil {
   390  			return addr, "", 0, nil
   391  		}
   392  	}
   393  
   394  	var k *keyring.Record
   395  	if err == nil {
   396  		k, err = kr.KeyByAddress(addr)
   397  		if err != nil {
   398  			return nil, "", 0, err
   399  		}
   400  	} else {
   401  		k, err = kr.Key(from)
   402  		if err != nil {
   403  			return nil, "", 0, err
   404  		}
   405  	}
   406  
   407  	addr, err = k.GetAddress()
   408  	if err != nil {
   409  		return nil, "", 0, err
   410  	}
   411  
   412  	return addr, k.Name, k.GetType(), nil
   413  }
   414  
   415  // NewKeyringFromBackend gets a Keyring object from a backend
   416  func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) {
   417  	if ctx.Simulate {
   418  		backend = keyring.BackendMemory
   419  	}
   420  
   421  	return keyring.New(sdk.KeyringServiceName(), backend, ctx.KeyringDir, ctx.Input, ctx.Codec, ctx.KeyringOptions...)
   422  }