282 lines
7.2 KiB
Go
282 lines
7.2 KiB
Go
package context
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
"github.com/tendermint/tendermint/libs/cli"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
tmlite "github.com/tendermint/tendermint/lite"
|
|
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
|
cskeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
|
"github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
var (
|
|
verifier tmlite.Verifier
|
|
)
|
|
|
|
// CLIContext implements a typical CLI context created in SDK modules for
|
|
// transaction handling and queries.
|
|
type CLIContext struct {
|
|
Codec *codec.Codec
|
|
AccDecoder auth.AccountDecoder
|
|
Client rpcclient.Client
|
|
Output io.Writer
|
|
OutputFormat string
|
|
Height int64
|
|
NodeURI string
|
|
From string
|
|
AccountStore string
|
|
TrustNode bool
|
|
UseLedger bool
|
|
Async bool
|
|
PrintResponse bool
|
|
Verifier tmlite.Verifier
|
|
Simulate bool
|
|
GenerateOnly bool
|
|
fromAddress types.AccAddress
|
|
fromName string
|
|
Indent bool
|
|
}
|
|
|
|
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
|
// command line using Viper.
|
|
func NewCLIContext() CLIContext {
|
|
var rpc rpcclient.Client
|
|
|
|
nodeURI := viper.GetString(client.FlagNode)
|
|
if nodeURI != "" {
|
|
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
|
|
}
|
|
|
|
from := viper.GetString(client.FlagFrom)
|
|
fromAddress, fromName := fromFields(from)
|
|
|
|
// We need to use a single verifier for all contexts
|
|
if verifier == nil {
|
|
verifier = createVerifier()
|
|
}
|
|
|
|
return CLIContext{
|
|
Client: rpc,
|
|
Output: os.Stdout,
|
|
NodeURI: nodeURI,
|
|
AccountStore: auth.StoreKey,
|
|
From: viper.GetString(client.FlagFrom),
|
|
OutputFormat: viper.GetString(cli.OutputFlag),
|
|
Height: viper.GetInt64(client.FlagHeight),
|
|
TrustNode: viper.GetBool(client.FlagTrustNode),
|
|
UseLedger: viper.GetBool(client.FlagUseLedger),
|
|
Async: viper.GetBool(client.FlagAsync),
|
|
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
|
Verifier: verifier,
|
|
Simulate: viper.GetBool(client.FlagDryRun),
|
|
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
|
fromAddress: fromAddress,
|
|
fromName: fromName,
|
|
Indent: viper.GetBool(client.FlagIndentResponse),
|
|
}
|
|
}
|
|
|
|
func createVerifier() tmlite.Verifier {
|
|
trustNodeDefined := viper.IsSet(client.FlagTrustNode)
|
|
if !trustNodeDefined {
|
|
return nil
|
|
}
|
|
|
|
trustNode := viper.GetBool(client.FlagTrustNode)
|
|
if trustNode {
|
|
return nil
|
|
}
|
|
|
|
chainID := viper.GetString(client.FlagChainID)
|
|
home := viper.GetString(cli.HomeFlag)
|
|
nodeURI := viper.GetString(client.FlagNode)
|
|
|
|
var errMsg bytes.Buffer
|
|
if chainID == "" {
|
|
errMsg.WriteString("--chain-id ")
|
|
}
|
|
if home == "" {
|
|
errMsg.WriteString("--home ")
|
|
}
|
|
if nodeURI == "" {
|
|
errMsg.WriteString("--node ")
|
|
}
|
|
if errMsg.Len() != 0 {
|
|
fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String())
|
|
os.Exit(1)
|
|
}
|
|
|
|
node := rpcclient.NewHTTP(nodeURI, "/websocket")
|
|
cacheSize := 10 // TODO: determine appropriate cache size
|
|
verifier, err := tmliteProxy.NewVerifier(
|
|
chainID, filepath.Join(home, ".gaialite"),
|
|
node, log.NewNopLogger(), cacheSize,
|
|
)
|
|
|
|
if err != nil {
|
|
fmt.Printf("Create verifier failed: %s\n", err.Error())
|
|
fmt.Printf("Please check network connection and verify the address of the node to connect to\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
return verifier
|
|
}
|
|
|
|
func fromFields(from string) (fromAddr types.AccAddress, fromName string) {
|
|
if from == "" {
|
|
return nil, ""
|
|
}
|
|
|
|
keybase, err := keys.GetKeyBase()
|
|
if err != nil {
|
|
fmt.Println("no keybase found")
|
|
os.Exit(1)
|
|
}
|
|
|
|
var info cskeys.Info
|
|
if addr, err := types.AccAddressFromBech32(from); err == nil {
|
|
info, err = keybase.GetByAddress(addr)
|
|
if err != nil {
|
|
fmt.Printf("could not find key %s\n", from)
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
info, err = keybase.Get(from)
|
|
if err != nil {
|
|
fmt.Printf("could not find key %s\n", from)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
fromAddr = info.GetAddress()
|
|
fromName = info.GetName()
|
|
return
|
|
}
|
|
|
|
// WithCodec returns a copy of the context with an updated codec.
|
|
func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext {
|
|
ctx.Codec = cdc
|
|
return ctx
|
|
}
|
|
|
|
// GetAccountDecoder gets the account decoder for auth.DefaultAccount.
|
|
func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder {
|
|
return func(accBytes []byte) (acct auth.Account, err error) {
|
|
err = cdc.UnmarshalBinaryBare(accBytes, &acct)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return acct, err
|
|
}
|
|
}
|
|
|
|
// WithAccountDecoder returns a copy of the context with an updated account
|
|
// decoder.
|
|
func (ctx CLIContext) WithAccountDecoder(cdc *codec.Codec) CLIContext {
|
|
ctx.AccDecoder = GetAccountDecoder(cdc)
|
|
return ctx
|
|
}
|
|
|
|
// WithOutput returns a copy of the context with an updated output writer (e.g. stdout).
|
|
func (ctx CLIContext) WithOutput(w io.Writer) CLIContext {
|
|
ctx.Output = w
|
|
return ctx
|
|
}
|
|
|
|
// WithAccountStore returns a copy of the context with an updated AccountStore.
|
|
func (ctx CLIContext) WithAccountStore(accountStore string) CLIContext {
|
|
ctx.AccountStore = accountStore
|
|
return ctx
|
|
}
|
|
|
|
// WithFrom returns a copy of the context with an updated from address or name.
|
|
func (ctx CLIContext) WithFrom(from string) CLIContext {
|
|
ctx.From = from
|
|
return ctx
|
|
}
|
|
|
|
// WithTrustNode returns a copy of the context with an updated TrustNode flag.
|
|
func (ctx CLIContext) WithTrustNode(trustNode bool) CLIContext {
|
|
ctx.TrustNode = trustNode
|
|
return ctx
|
|
}
|
|
|
|
// WithNodeURI returns a copy of the context with an updated node URI.
|
|
func (ctx CLIContext) WithNodeURI(nodeURI string) CLIContext {
|
|
ctx.NodeURI = nodeURI
|
|
ctx.Client = rpcclient.NewHTTP(nodeURI, "/websocket")
|
|
return ctx
|
|
}
|
|
|
|
// WithClient returns a copy of the context with an updated RPC client
|
|
// instance.
|
|
func (ctx CLIContext) WithClient(client rpcclient.Client) CLIContext {
|
|
ctx.Client = client
|
|
return ctx
|
|
}
|
|
|
|
// WithUseLedger returns a copy of the context with an updated UseLedger flag.
|
|
func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
|
|
ctx.UseLedger = useLedger
|
|
return ctx
|
|
}
|
|
|
|
// WithVerifier - return a copy of the context with an updated Verifier
|
|
func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext {
|
|
ctx.Verifier = verifier
|
|
return ctx
|
|
}
|
|
|
|
// WithGenerateOnly returns a copy of the context with updated GenerateOnly value
|
|
func (ctx CLIContext) WithGenerateOnly(generateOnly bool) CLIContext {
|
|
ctx.GenerateOnly = generateOnly
|
|
return ctx
|
|
}
|
|
|
|
// WithSimulation returns a copy of the context with updated Simulate value
|
|
func (ctx CLIContext) WithSimulation(simulate bool) CLIContext {
|
|
ctx.Simulate = simulate
|
|
return ctx
|
|
}
|
|
|
|
// PrintOutput prints output while respecting output and indent flags
|
|
// NOTE: pass in marshalled structs that have been unmarshaled
|
|
// because this function will panic on marshaling errors
|
|
func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) {
|
|
var out []byte
|
|
|
|
switch ctx.OutputFormat {
|
|
case "text":
|
|
out = []byte(toPrint.String())
|
|
case "json":
|
|
if ctx.Indent {
|
|
out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ")
|
|
} else {
|
|
out, err = ctx.Codec.MarshalJSON(toPrint)
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
fmt.Println(string(out))
|
|
return
|
|
}
|