github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/client/tx.go (about)

     1  package client
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/cosmos/gogoproto/jsonpb"
    13  
    14  	"github.com/cosmos/cosmos-sdk/client"
    15  	"github.com/cosmos/cosmos-sdk/client/tx"
    16  	"github.com/cosmos/cosmos-sdk/crypto/keyring"
    17  	sdk "github.com/cosmos/cosmos-sdk/types"
    18  	"github.com/cosmos/cosmos-sdk/types/errors"
    19  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    20  )
    21  
    22  // GasEstimateResponse defines a response definition for tx gas estimation.
    23  type GasEstimateResponse struct {
    24  	GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"`
    25  }
    26  
    27  func (gr GasEstimateResponse) String() string {
    28  	return fmt.Sprintf("gas estimate: %d", gr.GasEstimate)
    29  }
    30  
    31  // SignTx signs a transaction managed by the TxBuilder using a `name` key stored in Keybase.
    32  // The new signature is appended to the TxBuilder when overwrite=false or overwritten otherwise.
    33  // Don't perform online validation or lookups if offline is true.
    34  func SignTx(txFactory tx.Factory, clientCtx client.Context, name string, txBuilder client.TxBuilder, offline, overwriteSig bool) error {
    35  	k, err := txFactory.Keybase().Key(name)
    36  	if err != nil {
    37  		return err
    38  	}
    39  
    40  	// Ledger and Multisigs only support LEGACY_AMINO_JSON signing.
    41  	if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED &&
    42  		(k.GetType() == keyring.TypeLedger || k.GetType() == keyring.TypeMulti) {
    43  		txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
    44  	}
    45  
    46  	pubKey, err := k.GetPubKey()
    47  	if err != nil {
    48  		return err
    49  	}
    50  	addr := sdk.AccAddress(pubKey.Address())
    51  	signers, err := txBuilder.GetTx().GetSigners()
    52  	if err != nil {
    53  		return err
    54  	}
    55  	if !isTxSigner(addr, signers) {
    56  		return fmt.Errorf("%s: %s", errors.ErrorInvalidSigner, name)
    57  	}
    58  	if !offline {
    59  		txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
    60  		if err != nil {
    61  			return err
    62  		}
    63  	}
    64  
    65  	return tx.Sign(clientCtx.CmdContext, txFactory, name, txBuilder, overwriteSig)
    66  }
    67  
    68  // SignTxWithSignerAddress attaches a signature to a transaction.
    69  // Don't perform online validation or lookups if offline is true, else
    70  // populate account and sequence numbers from a foreign account.
    71  // This function should only be used when signing with a multisig. For
    72  // normal keys, please use SignTx directly.
    73  func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
    74  	name string, txBuilder client.TxBuilder, offline, overwrite bool,
    75  ) (err error) {
    76  	// Multisigs only support LEGACY_AMINO_JSON signing.
    77  	if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED {
    78  		txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
    79  	}
    80  
    81  	if !offline {
    82  		txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  
    88  	return tx.Sign(clientCtx.CmdContext, txFactory, name, txBuilder, overwrite)
    89  }
    90  
    91  // Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
    92  func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error) {
    93  	var bytes []byte
    94  
    95  	if filename == "-" {
    96  		bytes, err = io.ReadAll(os.Stdin)
    97  	} else {
    98  		bytes, err = os.ReadFile(filename)
    99  	}
   100  
   101  	if err != nil {
   102  		return
   103  	}
   104  
   105  	return ctx.TxConfig.TxJSONDecoder()(bytes)
   106  }
   107  
   108  // ReadTxsFromInput reads multiples txs from the given filename(s). Can pass "-" to read from stdin.
   109  // Unlike ReadTxFromFile, this function does not decode the txs.
   110  func ReadTxsFromInput(txCfg client.TxConfig, filenames ...string) (scanner *BatchScanner, err error) {
   111  	if len(filenames) == 0 {
   112  		return nil, fmt.Errorf("no file name provided")
   113  	}
   114  
   115  	var infile io.Reader = os.Stdin
   116  	if filenames[0] != "-" {
   117  		buf := new(bytes.Buffer)
   118  		for _, f := range filenames {
   119  			bytes, err := os.ReadFile(filepath.Clean(f))
   120  			if err != nil {
   121  				return nil, fmt.Errorf("couldn't read %s: %w", f, err)
   122  			}
   123  
   124  			if _, err := buf.WriteString(string(bytes)); err != nil {
   125  				return nil, fmt.Errorf("couldn't write to merged file: %w", err)
   126  			}
   127  		}
   128  
   129  		infile = buf
   130  	}
   131  
   132  	return NewBatchScanner(txCfg, infile), nil
   133  }
   134  
   135  // NewBatchScanner returns a new BatchScanner to read newline-delimited StdTx transactions from r.
   136  func NewBatchScanner(cfg client.TxConfig, r io.Reader) *BatchScanner {
   137  	return &BatchScanner{Scanner: bufio.NewScanner(r), cfg: cfg}
   138  }
   139  
   140  // BatchScanner provides a convenient interface for reading batch data such as a file
   141  // of newline-delimited JSON encoded StdTx.
   142  type BatchScanner struct {
   143  	*bufio.Scanner
   144  	theTx        sdk.Tx
   145  	cfg          client.TxConfig
   146  	unmarshalErr error
   147  }
   148  
   149  // Tx returns the most recent Tx unmarshalled by a call to Scan.
   150  func (bs BatchScanner) Tx() sdk.Tx { return bs.theTx }
   151  
   152  // UnmarshalErr returns the first unmarshalling error that was encountered by the scanner.
   153  func (bs BatchScanner) UnmarshalErr() error { return bs.unmarshalErr }
   154  
   155  // Scan advances the Scanner to the next line.
   156  func (bs *BatchScanner) Scan() bool {
   157  	if !bs.Scanner.Scan() {
   158  		return false
   159  	}
   160  
   161  	tx, err := bs.cfg.TxJSONDecoder()(bs.Bytes())
   162  	bs.theTx = tx
   163  	if err != nil && bs.unmarshalErr == nil {
   164  		bs.unmarshalErr = err
   165  		return false
   166  	}
   167  
   168  	return true
   169  }
   170  
   171  func populateAccountFromState(
   172  	txBldr tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
   173  ) (tx.Factory, error) {
   174  	num, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr)
   175  	if err != nil {
   176  		return txBldr, err
   177  	}
   178  
   179  	return txBldr.WithAccountNumber(num).WithSequence(seq), nil
   180  }
   181  
   182  func ParseQueryResponse(bz []byte) (sdk.SimulationResponse, error) {
   183  	var simRes sdk.SimulationResponse
   184  	if err := jsonpb.Unmarshal(strings.NewReader(string(bz)), &simRes); err != nil {
   185  		return sdk.SimulationResponse{}, err
   186  	}
   187  
   188  	return simRes, nil
   189  }
   190  
   191  func isTxSigner(user []byte, signers [][]byte) bool {
   192  	for _, s := range signers {
   193  		if bytes.Equal(user, s) {
   194  			return true
   195  		}
   196  	}
   197  
   198  	return false
   199  }