2020-01-24 08:40:56 -08:00
|
|
|
package client
|
2018-08-06 11:11:30 -07:00
|
|
|
|
|
|
|
import (
|
2019-06-22 02:24:59 -07:00
|
|
|
"bufio"
|
2018-09-07 03:59:16 -07:00
|
|
|
"bytes"
|
2018-08-22 04:38:55 -07:00
|
|
|
"fmt"
|
2020-06-08 08:19:29 -07:00
|
|
|
"io"
|
2019-02-26 03:34:27 -08:00
|
|
|
"io/ioutil"
|
2018-08-22 04:38:55 -07:00
|
|
|
"os"
|
2020-04-03 07:59:46 -07:00
|
|
|
"strings"
|
2018-08-22 04:38:55 -07:00
|
|
|
|
2020-04-03 07:59:46 -07:00
|
|
|
"github.com/gogo/protobuf/jsonpb"
|
2019-03-19 09:52:43 -07:00
|
|
|
|
2020-06-01 05:46:03 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
2020-07-29 15:33:42 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
2019-05-28 01:44:04 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
2021-03-31 02:25:23 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
2018-08-06 11:11:30 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2020-04-15 14:55:02 -07:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2021-03-31 02:25:23 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
2020-09-22 07:35:18 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
|
2018-08-06 11:11:30 -07:00
|
|
|
)
|
|
|
|
|
2019-02-04 15:35:12 -08:00
|
|
|
// GasEstimateResponse defines a response definition for tx gas estimation.
|
|
|
|
type GasEstimateResponse struct {
|
2019-07-05 16:25:56 -07:00
|
|
|
GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"`
|
2019-02-04 15:35:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gr GasEstimateResponse) String() string {
|
|
|
|
return fmt.Sprintf("gas estimate: %d", gr.GasEstimate)
|
|
|
|
}
|
|
|
|
|
2018-09-02 11:20:14 -07:00
|
|
|
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
|
2020-07-29 15:33:42 -07:00
|
|
|
func PrintUnsignedStdTx(txBldr tx.Factory, clientCtx client.Context, msgs []sdk.Msg) error {
|
2020-08-03 23:41:58 -07:00
|
|
|
err := tx.GenerateTx(clientCtx, txBldr, msgs...)
|
2020-07-29 15:33:42 -07:00
|
|
|
return err
|
2018-09-02 11:20:14 -07:00
|
|
|
}
|
|
|
|
|
2020-12-14 13:44:15 -08:00
|
|
|
// SignTx signs a transaction managed by the TxBuilder using a `name` key stored in Keybase.
|
|
|
|
// The new signature is appended to the TxBuilder when overwrite=false or overwritten otherwise.
|
2018-10-19 11:00:27 -07:00
|
|
|
// Don't perform online validation or lookups if offline is true.
|
2021-03-31 02:25:23 -07:00
|
|
|
func SignTx(txFactory tx.Factory, clientCtx client.Context, name string, txBuilder client.TxBuilder, offline, overwriteSig bool) error {
|
2020-07-29 15:33:42 -07:00
|
|
|
info, err := txFactory.Keybase().Key(name)
|
2018-09-07 03:59:16 -07:00
|
|
|
if err != nil {
|
2020-07-29 15:33:42 -07:00
|
|
|
return err
|
2018-09-07 03:59:16 -07:00
|
|
|
}
|
2021-03-31 02:25:23 -07:00
|
|
|
|
|
|
|
// Ledger and Multisigs only support LEGACY_AMINO_JSON signing.
|
|
|
|
if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED &&
|
|
|
|
(info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeMulti) {
|
2021-04-06 16:33:54 -07:00
|
|
|
txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
|
2021-03-31 02:25:23 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 15:33:42 -07:00
|
|
|
addr := sdk.AccAddress(info.GetPubKey().Address())
|
2021-03-31 02:25:23 -07:00
|
|
|
if !isTxSigner(addr, txBuilder.GetTx().GetSigners()) {
|
2020-07-29 15:33:42 -07:00
|
|
|
return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
|
2018-09-07 03:59:16 -07:00
|
|
|
}
|
2019-01-16 09:30:57 -08:00
|
|
|
if !offline {
|
2020-07-29 15:33:42 -07:00
|
|
|
txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
|
2018-09-07 03:59:16 -07:00
|
|
|
if err != nil {
|
2020-07-29 15:33:42 -07:00
|
|
|
return err
|
2018-09-07 03:59:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 02:25:23 -07:00
|
|
|
return tx.Sign(txFactory, name, txBuilder, overwriteSig)
|
2019-01-16 09:30:57 -08:00
|
|
|
}
|
|
|
|
|
2020-07-29 15:33:42 -07:00
|
|
|
// SignTxWithSignerAddress attaches a signature to a transaction.
|
2019-01-16 09:30:57 -08:00
|
|
|
// Don't perform online validation or lookups if offline is true, else
|
|
|
|
// populate account and sequence numbers from a foreign account.
|
2021-03-31 02:25:23 -07:00
|
|
|
// This function should only be used when signing with a multisig. For
|
|
|
|
// normal keys, please use SignTx directly.
|
2020-07-29 15:33:42 -07:00
|
|
|
func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
|
2020-12-14 13:44:15 -08:00
|
|
|
name string, txBuilder client.TxBuilder, offline, overwrite bool) (err error) {
|
2021-03-31 02:25:23 -07:00
|
|
|
// Multisigs only support LEGACY_AMINO_JSON signing.
|
|
|
|
if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED {
|
2021-04-06 16:33:54 -07:00
|
|
|
txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
|
2021-03-31 02:25:23 -07:00
|
|
|
}
|
2019-01-16 09:30:57 -08:00
|
|
|
|
|
|
|
// check whether the address is a signer
|
2020-07-29 15:33:42 -07:00
|
|
|
if !isTxSigner(addr, txBuilder.GetTx().GetSigners()) {
|
|
|
|
return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
|
2019-01-16 09:30:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if !offline {
|
2020-07-29 15:33:42 -07:00
|
|
|
txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
|
2018-09-07 03:59:16 -07:00
|
|
|
if err != nil {
|
2020-07-29 15:33:42 -07:00
|
|
|
return err
|
2018-09-07 03:59:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-14 13:44:15 -08:00
|
|
|
return tx.Sign(txFactory, name, txBuilder, overwrite)
|
2019-01-16 09:30:57 -08:00
|
|
|
}
|
|
|
|
|
2019-02-26 03:34:27 -08:00
|
|
|
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
|
2020-06-18 13:29:41 -07:00
|
|
|
func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error) {
|
2019-02-26 03:34:27 -08:00
|
|
|
var bytes []byte
|
2019-06-15 05:34:11 -07:00
|
|
|
|
2019-02-26 03:34:27 -08:00
|
|
|
if filename == "-" {
|
|
|
|
bytes, err = ioutil.ReadAll(os.Stdin)
|
|
|
|
} else {
|
|
|
|
bytes, err = ioutil.ReadFile(filename)
|
|
|
|
}
|
2019-06-15 05:34:11 -07:00
|
|
|
|
2019-02-26 03:34:27 -08:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-06-15 05:34:11 -07:00
|
|
|
|
2020-07-20 05:30:12 -07:00
|
|
|
return ctx.TxConfig.TxJSONDecoder()(bytes)
|
2019-02-26 03:34:27 -08:00
|
|
|
}
|
|
|
|
|
2020-06-08 08:19:29 -07:00
|
|
|
// NewBatchScanner returns a new BatchScanner to read newline-delimited StdTx transactions from r.
|
2020-07-29 15:33:42 -07:00
|
|
|
func NewBatchScanner(cfg client.TxConfig, r io.Reader) *BatchScanner {
|
|
|
|
return &BatchScanner{Scanner: bufio.NewScanner(r), cfg: cfg}
|
2020-06-08 08:19:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// BatchScanner provides a convenient interface for reading batch data such as a file
|
|
|
|
// of newline-delimited JSON encoded StdTx.
|
|
|
|
type BatchScanner struct {
|
|
|
|
*bufio.Scanner
|
2020-07-29 15:33:42 -07:00
|
|
|
theTx sdk.Tx
|
|
|
|
cfg client.TxConfig
|
2020-06-08 08:19:29 -07:00
|
|
|
unmarshalErr error
|
|
|
|
}
|
|
|
|
|
2020-07-29 15:33:42 -07:00
|
|
|
// Tx returns the most recent Tx unmarshalled by a call to Scan.
|
|
|
|
func (bs BatchScanner) Tx() sdk.Tx { return bs.theTx }
|
2020-06-08 08:19:29 -07:00
|
|
|
|
|
|
|
// UnmarshalErr returns the first unmarshalling error that was encountered by the scanner.
|
|
|
|
func (bs BatchScanner) UnmarshalErr() error { return bs.unmarshalErr }
|
|
|
|
|
|
|
|
// Scan advances the Scanner to the next line.
|
|
|
|
func (bs *BatchScanner) Scan() bool {
|
|
|
|
if !bs.Scanner.Scan() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-29 15:33:42 -07:00
|
|
|
tx, err := bs.cfg.TxJSONDecoder()(bs.Bytes())
|
|
|
|
bs.theTx = tx
|
|
|
|
if err != nil && bs.unmarshalErr == nil {
|
2020-06-08 08:19:29 -07:00
|
|
|
bs.unmarshalErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-03-26 07:36:10 -07:00
|
|
|
func populateAccountFromState(
|
2020-07-29 15:33:42 -07:00
|
|
|
txBldr tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
|
|
|
|
) (tx.Factory, error) {
|
2019-03-26 07:36:10 -07:00
|
|
|
|
2020-06-18 13:29:41 -07:00
|
|
|
num, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr)
|
2019-03-26 07:36:10 -07:00
|
|
|
if err != nil {
|
|
|
|
return txBldr, err
|
2019-01-16 09:30:57 -08:00
|
|
|
}
|
|
|
|
|
2019-06-19 05:24:11 -07:00
|
|
|
return txBldr.WithAccountNumber(num).WithSequence(seq), nil
|
2018-09-07 03:59:16 -07:00
|
|
|
}
|
|
|
|
|
2018-12-12 13:29:42 -08:00
|
|
|
// GetTxEncoder return tx encoder from global sdk configuration if ones is defined.
|
|
|
|
// Otherwise returns encoder with default logic.
|
2020-08-10 12:41:21 -07:00
|
|
|
func GetTxEncoder(cdc *codec.LegacyAmino) (encoder sdk.TxEncoder) {
|
2018-12-12 13:29:42 -08:00
|
|
|
encoder = sdk.GetConfig().GetTxEncoder()
|
|
|
|
if encoder == nil {
|
2020-09-22 07:35:18 -07:00
|
|
|
encoder = legacytx.DefaultTxEncoder(cdc)
|
2018-12-12 13:29:42 -08:00
|
|
|
}
|
2019-06-15 05:34:11 -07:00
|
|
|
|
|
|
|
return encoder
|
2018-12-12 13:29:42 -08:00
|
|
|
}
|
|
|
|
|
2020-07-30 08:36:36 -07:00
|
|
|
func ParseQueryResponse(bz []byte) (sdk.SimulationResponse, error) {
|
2020-03-19 18:40:07 -07:00
|
|
|
var simRes sdk.SimulationResponse
|
2020-04-03 07:59:46 -07:00
|
|
|
if err := jsonpb.Unmarshal(strings.NewReader(string(bz)), &simRes); err != nil {
|
2020-03-19 18:40:07 -07:00
|
|
|
return sdk.SimulationResponse{}, err
|
2018-08-22 04:38:55 -07:00
|
|
|
}
|
2019-06-15 05:34:11 -07:00
|
|
|
|
2020-03-19 18:40:07 -07:00
|
|
|
return simRes, nil
|
2018-08-22 04:38:55 -07:00
|
|
|
}
|
2018-08-31 10:04:11 -07:00
|
|
|
|
2018-09-07 03:59:16 -07:00
|
|
|
func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool {
|
|
|
|
for _, s := range signers {
|
|
|
|
if bytes.Equal(user.Bytes(), s.Bytes()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2019-01-29 11:22:47 -08:00
|
|
|
|
2018-09-07 03:59:16 -07:00
|
|
|
return false
|
|
|
|
}
|