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