2020-06-01 05:46:03 -07:00
|
|
|
package client
|
2018-08-06 11:11:30 -07:00
|
|
|
|
|
|
|
import (
|
2020-06-18 00:36:25 -07:00
|
|
|
"encoding/json"
|
2018-09-02 11:20:14 -07:00
|
|
|
"io"
|
2018-10-03 08:48:23 -07:00
|
|
|
"os"
|
2018-09-02 11:20:14 -07:00
|
|
|
|
2020-08-11 00:19:49 -07:00
|
|
|
"github.com/gogo/protobuf/proto"
|
|
|
|
|
2019-04-16 10:02:36 -07:00
|
|
|
"github.com/pkg/errors"
|
2018-08-30 00:52:17 -07:00
|
|
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
2020-04-22 12:21:48 -07:00
|
|
|
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
2020-07-21 08:28:43 -07:00
|
|
|
yaml "gopkg.in/yaml.v2"
|
2018-12-10 06:27:25 -08:00
|
|
|
|
2019-06-05 16:26:17 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
2020-07-21 08:28:43 -07:00
|
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
2020-03-25 08:20:36 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
2019-01-29 11:22:47 -08:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2018-08-06 11:11:30 -07:00
|
|
|
)
|
|
|
|
|
2020-06-15 10:39:09 -07:00
|
|
|
// Context implements a typical context created in SDK modules for transaction
|
|
|
|
// handling and queries.
|
2020-06-01 05:46:03 -07:00
|
|
|
type Context struct {
|
2020-07-03 09:42:12 -07:00
|
|
|
FromAddress sdk.AccAddress
|
|
|
|
Client rpcclient.Client
|
|
|
|
ChainID string
|
|
|
|
JSONMarshaler codec.JSONMarshaler
|
|
|
|
InterfaceRegistry codectypes.InterfaceRegistry
|
|
|
|
Input io.Reader
|
|
|
|
Keyring keyring.Keyring
|
|
|
|
Output io.Writer
|
|
|
|
OutputFormat string
|
|
|
|
Height int64
|
|
|
|
HomeDir string
|
|
|
|
From string
|
|
|
|
BroadcastMode string
|
|
|
|
FromName string
|
|
|
|
UseLedger bool
|
|
|
|
Simulate bool
|
|
|
|
GenerateOnly bool
|
|
|
|
Offline bool
|
|
|
|
SkipConfirm bool
|
2020-07-20 05:30:12 -07:00
|
|
|
TxConfig TxConfig
|
2020-07-03 09:42:12 -07:00
|
|
|
AccountRetriever AccountRetriever
|
|
|
|
NodeURI string
|
2020-06-15 10:39:09 -07:00
|
|
|
|
2020-03-13 10:59:14 -07:00
|
|
|
// TODO: Deprecated (remove).
|
2020-08-10 12:41:21 -07:00
|
|
|
LegacyAmino *codec.LegacyAmino
|
2018-08-06 11:11:30 -07:00
|
|
|
}
|
|
|
|
|
2020-04-08 02:38:28 -07:00
|
|
|
// WithKeyring returns a copy of the context with an updated keyring.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithKeyring(k keyring.Keyring) Context {
|
2020-04-08 02:38:28 -07:00
|
|
|
ctx.Keyring = k
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2019-11-14 06:17:21 -08:00
|
|
|
// WithInput returns a copy of the context with an updated input.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithInput(r io.Reader) Context {
|
2019-11-14 06:17:21 -08:00
|
|
|
ctx.Input = r
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-06-01 05:46:03 -07:00
|
|
|
// WithJSONMarshaler returns a copy of the Context with an updated JSONMarshaler.
|
|
|
|
func (ctx Context) WithJSONMarshaler(m codec.JSONMarshaler) Context {
|
2020-05-21 14:29:34 -07:00
|
|
|
ctx.JSONMarshaler = m
|
2020-03-13 10:59:14 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-08-26 02:39:38 -07:00
|
|
|
// WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec.
|
2020-03-13 10:59:14 -07:00
|
|
|
// TODO: Deprecated (remove).
|
2020-08-10 12:41:21 -07:00
|
|
|
func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context {
|
|
|
|
ctx.LegacyAmino = cdc
|
2018-08-06 11:11:30 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2018-09-28 21:45:54 -07:00
|
|
|
// WithOutput returns a copy of the context with an updated output writer (e.g. stdout).
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithOutput(w io.Writer) Context {
|
2018-09-28 21:45:54 -07:00
|
|
|
ctx.Output = w
|
2018-08-06 11:11:30 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2018-09-25 13:48:38 -07:00
|
|
|
// WithFrom returns a copy of the context with an updated from address or name.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithFrom(from string) Context {
|
2018-09-25 13:48:38 -07:00
|
|
|
ctx.From = from
|
2018-08-06 11:11:30 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-07-10 07:45:46 -07:00
|
|
|
// WithOutputFormat returns a copy of the context with an updated OutputFormat field.
|
|
|
|
func (ctx Context) WithOutputFormat(format string) Context {
|
|
|
|
ctx.OutputFormat = format
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// WithNodeURI returns a copy of the context with an updated node URI.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithNodeURI(nodeURI string) Context {
|
2018-08-06 11:11:30 -07:00
|
|
|
ctx.NodeURI = nodeURI
|
2020-04-22 12:21:48 -07:00
|
|
|
client, err := rpchttp.New(nodeURI, "/websocket")
|
2020-01-16 13:46:51 -08:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-04-22 12:21:48 -07:00
|
|
|
|
2020-01-16 13:46:51 -08:00
|
|
|
ctx.Client = client
|
2018-08-06 11:11:30 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2019-06-07 06:02:07 -07:00
|
|
|
// WithHeight returns a copy of the context with an updated height.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithHeight(height int64) Context {
|
2019-06-07 06:02:07 -07:00
|
|
|
ctx.Height = height
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// WithClient returns a copy of the context with an updated RPC client
|
|
|
|
// instance.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithClient(client rpcclient.Client) Context {
|
2018-08-06 11:11:30 -07:00
|
|
|
ctx.Client = client
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithUseLedger returns a copy of the context with an updated UseLedger flag.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithUseLedger(useLedger bool) Context {
|
2018-08-06 11:11:30 -07:00
|
|
|
ctx.UseLedger = useLedger
|
|
|
|
return ctx
|
|
|
|
}
|
2018-08-29 21:50:41 -07:00
|
|
|
|
2019-10-02 17:29:28 -07:00
|
|
|
// WithChainID returns a copy of the context with an updated chain ID.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithChainID(chainID string) Context {
|
2019-10-02 17:29:28 -07:00
|
|
|
ctx.ChainID = chainID
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-06-15 10:39:09 -07:00
|
|
|
// WithHomeDir returns a copy of the Context with HomeDir set.
|
|
|
|
func (ctx Context) WithHomeDir(dir string) Context {
|
|
|
|
ctx.HomeDir = dir
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2018-12-11 06:02:26 -08:00
|
|
|
// WithGenerateOnly returns a copy of the context with updated GenerateOnly value
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithGenerateOnly(generateOnly bool) Context {
|
2018-12-11 06:02:26 -08:00
|
|
|
ctx.GenerateOnly = generateOnly
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithSimulation returns a copy of the context with updated Simulate value
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithSimulation(simulate bool) Context {
|
2018-12-11 06:02:26 -08:00
|
|
|
ctx.Simulate = simulate
|
|
|
|
return ctx
|
|
|
|
}
|
2019-01-22 09:28:39 -08:00
|
|
|
|
2020-06-30 13:59:21 -07:00
|
|
|
// WithOffline returns a copy of the context with updated Offline value.
|
|
|
|
func (ctx Context) WithOffline(offline bool) Context {
|
|
|
|
ctx.Offline = offline
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2019-01-29 11:22:47 -08:00
|
|
|
// WithFromName returns a copy of the context with an updated from account name.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithFromName(name string) Context {
|
2019-01-29 11:22:47 -08:00
|
|
|
ctx.FromName = name
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithFromAddress returns a copy of the context with an updated from account
|
|
|
|
// address.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context {
|
2019-01-29 11:22:47 -08:00
|
|
|
ctx.FromAddress = addr
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2019-03-25 17:54:23 -07:00
|
|
|
// WithBroadcastMode returns a copy of the context with an updated broadcast
|
|
|
|
// mode.
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithBroadcastMode(mode string) Context {
|
2019-03-25 17:54:23 -07:00
|
|
|
ctx.BroadcastMode = mode
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-06-30 13:59:21 -07:00
|
|
|
// WithSkipConfirmation returns a copy of the context with an updated SkipConfirm
|
|
|
|
// value.
|
|
|
|
func (ctx Context) WithSkipConfirmation(skip bool) Context {
|
|
|
|
ctx.SkipConfirm = skip
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-07-20 05:30:12 -07:00
|
|
|
// WithTxConfig returns the context with an updated TxConfig
|
|
|
|
func (ctx Context) WithTxConfig(generator TxConfig) Context {
|
|
|
|
ctx.TxConfig = generator
|
2020-05-21 14:29:34 -07:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithAccountRetriever returns the context with an updated AccountRetriever
|
2020-06-01 05:46:03 -07:00
|
|
|
func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context {
|
2020-05-21 14:29:34 -07:00
|
|
|
ctx.AccountRetriever = retriever
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-07-03 09:42:12 -07:00
|
|
|
// WithInterfaceRegistry returns the context with an updated InterfaceRegistry
|
|
|
|
func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context {
|
|
|
|
ctx.InterfaceRegistry = interfaceRegistry
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2020-07-29 15:33:42 -07:00
|
|
|
// PrintString prints the raw string to ctx.Output or os.Stdout
|
|
|
|
func (ctx Context) PrintString(str string) error {
|
|
|
|
writer := ctx.Output
|
|
|
|
if writer == nil {
|
|
|
|
writer = os.Stdout
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := writer.Write([]byte(str))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:36:25 -07:00
|
|
|
// PrintOutput outputs toPrint to the ctx.Output based on ctx.OutputFormat which is
|
2020-03-25 09:24:13 -07:00
|
|
|
// either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint
|
2020-05-21 14:29:34 -07:00
|
|
|
// will be JSON encoded using ctx.JSONMarshaler. An error is returned upon failure.
|
2020-08-11 00:19:49 -07:00
|
|
|
func (ctx Context) PrintOutput(toPrint proto.Message) error {
|
2020-08-26 02:39:38 -07:00
|
|
|
// always serialize JSON initially because proto json can't be directly YAML encoded
|
|
|
|
out, err := ctx.JSONMarshaler.MarshalJSON(toPrint)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ctx.printOutput(out)
|
2020-08-11 00:19:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// PrintOutputLegacy is a variant of PrintOutput that doesn't require a proto type
|
|
|
|
// and uses amino JSON encoding. It will be removed in the near future!
|
|
|
|
func (ctx Context) PrintOutputLegacy(toPrint interface{}) error {
|
2020-08-26 02:39:38 -07:00
|
|
|
out, err := ctx.LegacyAmino.MarshalJSON(toPrint)
|
2020-06-18 00:36:25 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-26 02:39:38 -07:00
|
|
|
return ctx.printOutput(out)
|
|
|
|
}
|
2020-03-25 09:24:13 -07:00
|
|
|
|
2020-08-26 02:39:38 -07:00
|
|
|
func (ctx Context) printOutput(out []byte) error {
|
2020-06-18 00:36:25 -07:00
|
|
|
if ctx.OutputFormat == "text" {
|
|
|
|
// handle text format by decoding and re-encoding JSON as YAML
|
|
|
|
var j interface{}
|
2020-06-30 13:59:21 -07:00
|
|
|
|
2020-08-26 02:39:38 -07:00
|
|
|
err := json.Unmarshal(out, &j)
|
2020-06-18 00:36:25 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-03-25 09:24:13 -07:00
|
|
|
|
2020-06-18 00:36:25 -07:00
|
|
|
out, err = yaml.Marshal(j)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-03-25 09:24:13 -07:00
|
|
|
}
|
|
|
|
|
2020-06-18 00:36:25 -07:00
|
|
|
writer := ctx.Output
|
|
|
|
if writer == nil {
|
|
|
|
writer = os.Stdout
|
|
|
|
}
|
|
|
|
|
2020-08-26 02:39:38 -07:00
|
|
|
_, err := writer.Write(out)
|
2020-03-25 09:24:13 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:36:25 -07:00
|
|
|
if ctx.OutputFormat != "text" {
|
|
|
|
// append new-line for formats besides YAML
|
|
|
|
_, err = writer.Write([]byte("\n"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-01-22 09:28:39 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-19 06:21:38 -07:00
|
|
|
return nil
|
2019-01-22 09:28:39 -08:00
|
|
|
}
|
2019-01-29 11:22:47 -08:00
|
|
|
|
|
|
|
// GetFromFields returns a from account address and Keybase name given either
|
2019-03-25 08:27:24 -07:00
|
|
|
// an address or key name. If genOnly is true, only a valid Bech32 cosmos
|
|
|
|
// address is returned.
|
2020-04-08 02:38:28 -07:00
|
|
|
func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, error) {
|
2019-01-29 11:22:47 -08:00
|
|
|
if from == "" {
|
|
|
|
return nil, "", nil
|
|
|
|
}
|
|
|
|
|
2019-03-25 08:27:24 -07:00
|
|
|
if genOnly {
|
|
|
|
addr, err := sdk.AccAddressFromBech32(from)
|
|
|
|
if err != nil {
|
2020-03-18 18:49:33 -07:00
|
|
|
return nil, "", errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode")
|
2019-03-25 08:27:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return addr, "", nil
|
|
|
|
}
|
|
|
|
|
2020-03-25 08:20:36 -07:00
|
|
|
var info keyring.Info
|
2019-01-29 11:22:47 -08:00
|
|
|
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
|
2020-04-08 02:38:28 -07:00
|
|
|
info, err = kr.KeyByAddress(addr)
|
2019-01-29 11:22:47 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
} else {
|
2020-04-08 02:38:28 -07:00
|
|
|
info, err = kr.Key(from)
|
2019-01-29 11:22:47 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return info.GetAddress(), info.GetName(), nil
|
|
|
|
}
|
2020-04-08 02:38:28 -07:00
|
|
|
|
2020-06-30 13:59:21 -07:00
|
|
|
func newKeyringFromFlags(ctx Context, backend string) (keyring.Keyring, error) {
|
|
|
|
if ctx.GenerateOnly {
|
|
|
|
return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, ctx.HomeDir, ctx.Input)
|
2020-04-08 02:38:28 -07:00
|
|
|
}
|
2020-06-30 13:59:21 -07:00
|
|
|
|
|
|
|
return keyring.New(sdk.KeyringServiceName(), backend, ctx.HomeDir, ctx.Input)
|
2020-04-08 02:38:28 -07:00
|
|
|
}
|