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

     1  package client
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"io"
     7  	"os"
     8  
     9  	"github.com/spf13/viper"
    10  
    11  	"gopkg.in/yaml.v2"
    12  
    13  	rpcclient "github.com/Finschia/ostracon/rpc/client"
    14  	"github.com/gogo/protobuf/proto"
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/Finschia/finschia-sdk/codec"
    18  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    19  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    20  	sdk "github.com/Finschia/finschia-sdk/types"
    21  )
    22  
    23  // Context implements a typical context created in SDK modules for transaction
    24  // handling and queries.
    25  type Context struct {
    26  	FromAddress sdk.AccAddress
    27  	Client      rpcclient.Client
    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  // WithUseLedger returns a copy of the context with an updated UseLedger flag.
   151  func (ctx Context) WithUseLedger(useLedger bool) Context {
   152  	ctx.UseLedger = useLedger
   153  	return ctx
   154  }
   155  
   156  // WithChainID returns a copy of the context with an updated chain ID.
   157  func (ctx Context) WithChainID(chainID string) Context {
   158  	ctx.ChainID = chainID
   159  	return ctx
   160  }
   161  
   162  // WithHomeDir returns a copy of the Context with HomeDir set.
   163  func (ctx Context) WithHomeDir(dir string) Context {
   164  	if dir != "" {
   165  		ctx.HomeDir = dir
   166  	}
   167  	return ctx
   168  }
   169  
   170  // WithKeyringDir returns a copy of the Context with KeyringDir set.
   171  func (ctx Context) WithKeyringDir(dir string) Context {
   172  	ctx.KeyringDir = dir
   173  	return ctx
   174  }
   175  
   176  // WithGenerateOnly returns a copy of the context with updated GenerateOnly value
   177  func (ctx Context) WithGenerateOnly(generateOnly bool) Context {
   178  	ctx.GenerateOnly = generateOnly
   179  	return ctx
   180  }
   181  
   182  // WithSimulation returns a copy of the context with updated Simulate value
   183  func (ctx Context) WithSimulation(simulate bool) Context {
   184  	ctx.Simulate = simulate
   185  	return ctx
   186  }
   187  
   188  // WithOffline returns a copy of the context with updated Offline value.
   189  func (ctx Context) WithOffline(offline bool) Context {
   190  	ctx.Offline = offline
   191  	return ctx
   192  }
   193  
   194  // WithFromName returns a copy of the context with an updated from account name.
   195  func (ctx Context) WithFromName(name string) Context {
   196  	ctx.FromName = name
   197  	return ctx
   198  }
   199  
   200  // WithFromAddress returns a copy of the context with an updated from account
   201  // address.
   202  func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context {
   203  	ctx.FromAddress = addr
   204  	return ctx
   205  }
   206  
   207  // WithBroadcastMode returns a copy of the context with an updated broadcast
   208  // mode.
   209  func (ctx Context) WithBroadcastMode(mode string) Context {
   210  	ctx.BroadcastMode = mode
   211  	return ctx
   212  }
   213  
   214  // WithSignModeStr returns a copy of the context with an updated SignMode
   215  // value.
   216  func (ctx Context) WithSignModeStr(signModeStr string) Context {
   217  	ctx.SignModeStr = signModeStr
   218  	return ctx
   219  }
   220  
   221  // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm
   222  // value.
   223  func (ctx Context) WithSkipConfirmation(skip bool) Context {
   224  	ctx.SkipConfirm = skip
   225  	return ctx
   226  }
   227  
   228  // WithTxConfig returns the context with an updated TxConfig
   229  func (ctx Context) WithTxConfig(generator TxConfig) Context {
   230  	ctx.TxConfig = generator
   231  	return ctx
   232  }
   233  
   234  // WithAccountRetriever returns the context with an updated AccountRetriever
   235  func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context {
   236  	ctx.AccountRetriever = retriever
   237  	return ctx
   238  }
   239  
   240  // WithInterfaceRegistry returns the context with an updated InterfaceRegistry
   241  func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context {
   242  	ctx.InterfaceRegistry = interfaceRegistry
   243  	return ctx
   244  }
   245  
   246  // WithViper returns the context with Viper field. This Viper instance is used to read
   247  // client-side config from the config file.
   248  func (ctx Context) WithViper(prefix string) Context {
   249  	v := viper.New()
   250  	v.SetEnvPrefix(prefix)
   251  	v.AutomaticEnv()
   252  	ctx.Viper = v
   253  	return ctx
   254  }
   255  
   256  // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout
   257  func (ctx Context) PrintString(str string) error {
   258  	return ctx.PrintBytes([]byte(str))
   259  }
   260  
   261  // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout.
   262  // NOTE: for printing a complex state object, you should use ctx.PrintOutput
   263  func (ctx Context) PrintBytes(o []byte) error {
   264  	writer := ctx.Output
   265  	if writer == nil {
   266  		writer = os.Stdout
   267  	}
   268  
   269  	_, err := writer.Write(o)
   270  	return err
   271  }
   272  
   273  // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is
   274  // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint
   275  // will be JSON encoded using ctx.Codec. An error is returned upon failure.
   276  func (ctx Context) PrintProto(toPrint proto.Message) error {
   277  	// always serialize JSON initially because proto json can't be directly YAML encoded
   278  	out, err := ctx.Codec.MarshalJSON(toPrint)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	return ctx.printOutput(out)
   283  }
   284  
   285  // PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type
   286  // and uses amino JSON encoding.
   287  // Deprecated: It will be removed in the near future!
   288  func (ctx Context) PrintObjectLegacy(toPrint interface{}) error {
   289  	out, err := ctx.LegacyAmino.MarshalJSON(toPrint)
   290  	if err != nil {
   291  		return err
   292  	}
   293  	return ctx.printOutput(out)
   294  }
   295  
   296  func (ctx Context) printOutput(out []byte) error {
   297  	if ctx.OutputFormat == "text" {
   298  		// handle text format by decoding and re-encoding JSON as YAML
   299  		var j interface{}
   300  
   301  		err := json.Unmarshal(out, &j)
   302  		if err != nil {
   303  			return err
   304  		}
   305  
   306  		out, err = yaml.Marshal(j)
   307  		if err != nil {
   308  			return err
   309  		}
   310  	}
   311  
   312  	writer := ctx.Output
   313  	if writer == nil {
   314  		writer = os.Stdout
   315  	}
   316  
   317  	_, err := writer.Write(out)
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	if ctx.OutputFormat != "text" {
   323  		// append new-line for formats besides YAML
   324  		_, err = writer.Write([]byte("\n"))
   325  		if err != nil {
   326  			return err
   327  		}
   328  	}
   329  
   330  	return nil
   331  }
   332  
   333  // GetFromFields returns a from account address, account name and keyring type, given either
   334  // an address or key name. If genOnly is true, only a valid Bech32 cosmos
   335  // address is returned.
   336  func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, keyring.KeyType, error) {
   337  	if from == "" {
   338  		return nil, "", 0, nil
   339  	}
   340  
   341  	if genOnly {
   342  		addr, err := sdk.AccAddressFromBech32(from)
   343  		if err != nil {
   344  			return nil, "", 0, errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode")
   345  		}
   346  
   347  		return addr, "", 0, nil
   348  	}
   349  
   350  	var info keyring.Info
   351  	if addr, err := sdk.AccAddressFromBech32(from); err == nil {
   352  		info, err = kr.KeyByAddress(addr)
   353  		if err != nil {
   354  			return nil, "", 0, err
   355  		}
   356  	} else {
   357  		info, err = kr.Key(from)
   358  		if err != nil {
   359  			return nil, "", 0, err
   360  		}
   361  	}
   362  
   363  	return info.GetAddress(), info.GetName(), info.GetType(), nil
   364  }
   365  
   366  // NewKeyringFromBackend gets a Keyring object from a backend
   367  func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) {
   368  	if ctx.GenerateOnly || ctx.Simulate {
   369  		return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, ctx.KeyringDir, ctx.Input)
   370  	}
   371  
   372  	return keyring.New(sdk.KeyringServiceName(), backend, ctx.KeyringDir, ctx.Input, ctx.KeyringOptions...)
   373  }