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 }