github.com/Finschia/finschia-sdk@v0.49.1/client/context.go (about)

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