github.com/Finschia/finschia-sdk@v0.49.1/client/tx/tx.go (about) 1 package tx 2 3 import ( 4 "bufio" 5 "context" 6 "errors" 7 "fmt" 8 "os" 9 10 gogogrpc "github.com/gogo/protobuf/grpc" 11 "github.com/spf13/pflag" 12 13 "github.com/Finschia/finschia-sdk/client" 14 "github.com/Finschia/finschia-sdk/client/input" 15 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 16 sdk "github.com/Finschia/finschia-sdk/types" 17 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 18 "github.com/Finschia/finschia-sdk/types/tx" 19 "github.com/Finschia/finschia-sdk/types/tx/signing" 20 authsigning "github.com/Finschia/finschia-sdk/x/auth/signing" 21 ) 22 23 // GenerateOrBroadcastTxCLI will either generate and print and unsigned transaction 24 // or sign it and broadcast it returning an error upon failure. 25 func GenerateOrBroadcastTxCLI(clientCtx client.Context, flagSet *pflag.FlagSet, msgs ...sdk.Msg) error { 26 txf := NewFactoryCLI(clientCtx, flagSet) 27 return GenerateOrBroadcastTxWithFactory(clientCtx, txf, msgs...) 28 } 29 30 // GenerateOrBroadcastTxWithFactory will either generate and print and unsigned transaction 31 // or sign it and broadcast it returning an error upon failure. 32 func GenerateOrBroadcastTxWithFactory(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { 33 // Validate all msgs before generating or broadcasting the tx. 34 // We were calling ValidateBasic separately in each CLI handler before. 35 // Right now, we're factorizing that call inside this function. 36 // ref: https://github.com/cosmos/cosmos-sdk/pull/9236#discussion_r623803504 37 for _, msg := range msgs { 38 if err := msg.ValidateBasic(); err != nil { 39 return err 40 } 41 } 42 43 if clientCtx.GenerateOnly { 44 return GenerateTx(clientCtx, txf, msgs...) 45 } 46 47 return BroadcastTx(clientCtx, txf, msgs...) 48 } 49 50 // GenerateTx will generate an unsigned transaction and print it to the writer 51 // specified by ctx.Output. If simulation was requested, the gas will be 52 // simulated and also printed to the same writer before the transaction is 53 // printed. 54 func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { 55 if txf.SimulateAndExecute() { 56 if clientCtx.Offline { 57 return errors.New("cannot estimate gas in offline mode") 58 } 59 60 _, adjusted, err := CalculateGas(clientCtx, txf, msgs...) 61 if err != nil { 62 return err 63 } 64 65 txf = txf.WithGas(adjusted) 66 _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) 67 } 68 69 tx, err := BuildUnsignedTx(txf, msgs...) 70 if err != nil { 71 return err 72 } 73 74 json, err := clientCtx.TxConfig.TxJSONEncoder()(tx.GetTx()) 75 if err != nil { 76 return err 77 } 78 79 return clientCtx.PrintString(fmt.Sprintf("%s\n", json)) 80 } 81 82 // BroadcastTx attempts to generate, sign and broadcast a transaction with the 83 // given set of messages. It will also simulate gas requirements if necessary. 84 // It will return an error upon failure. 85 func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { 86 txf, err := prepareFactory(clientCtx, txf) 87 if err != nil { 88 return err 89 } 90 91 if txf.SimulateAndExecute() || clientCtx.Simulate { 92 _, adjusted, err := CalculateGas(clientCtx, txf, msgs...) 93 if err != nil { 94 return err 95 } 96 97 txf = txf.WithGas(adjusted) 98 _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) 99 } 100 101 if clientCtx.Simulate { 102 return nil 103 } 104 105 tx, err := BuildUnsignedTx(txf, msgs...) 106 if err != nil { 107 return err 108 } 109 110 if !clientCtx.SkipConfirm { 111 out, err := clientCtx.TxConfig.TxJSONEncoder()(tx.GetTx()) 112 if err != nil { 113 return err 114 } 115 116 _, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out) 117 118 buf := bufio.NewReader(os.Stdin) 119 ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr) 120 121 if err != nil || !ok { 122 _, _ = fmt.Fprintf(os.Stderr, "%s\n", "canceled transaction") 123 return err 124 } 125 } 126 127 tx.SetFeeGranter(clientCtx.GetFeeGranterAddress()) 128 err = Sign(txf, clientCtx.GetFromName(), tx, true) 129 if err != nil { 130 return err 131 } 132 133 txBytes, err := clientCtx.TxConfig.TxEncoder()(tx.GetTx()) 134 if err != nil { 135 return err 136 } 137 138 // broadcast to a Tendermint node 139 res, err := clientCtx.BroadcastTx(txBytes) 140 if err != nil { 141 return err 142 } 143 144 return clientCtx.PrintProto(res) 145 } 146 147 // BuildUnsignedTx builds a transaction to be signed given a set of messages. The 148 // transaction is initially created via the provided factory's generator. Once 149 // created, the fee, memo, and messages are set. 150 func BuildUnsignedTx(txf Factory, msgs ...sdk.Msg) (client.TxBuilder, error) { 151 return txf.BuildUnsignedTx(msgs...) 152 } 153 154 // BuildSimTx creates an unsigned tx with an empty single signature and returns 155 // the encoded transaction or an error if the unsigned transaction cannot be 156 // built. 157 func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) { 158 return txf.BuildSimTx(msgs...) 159 } 160 161 // CalculateGas simulates the execution of a transaction and returns the 162 // simulation response obtained by the query and the adjusted gas amount. 163 func CalculateGas( 164 clientCtx gogogrpc.ClientConn, txf Factory, msgs ...sdk.Msg, 165 ) (*tx.SimulateResponse, uint64, error) { 166 txBytes, err := BuildSimTx(txf, msgs...) 167 if err != nil { 168 return nil, 0, err 169 } 170 171 txSvcClient := tx.NewServiceClient(clientCtx) 172 simRes, err := txSvcClient.Simulate(context.Background(), &tx.SimulateRequest{ 173 TxBytes: txBytes, 174 }) 175 if err != nil { 176 return nil, 0, err 177 } 178 179 return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasInfo.GasUsed)), nil 180 } 181 182 // prepareFactory ensures the account defined by ctx.GetFromAddress() exists and 183 // if the account number and/or the account sequence number are zero (not set), 184 // they will be queried for and set on the provided Factory. A new Factory with 185 // the updated fields will be returned. 186 func prepareFactory(clientCtx client.Context, txf Factory) (Factory, error) { 187 from := clientCtx.GetFromAddress() 188 189 if err := txf.accountRetriever.EnsureExists(clientCtx, from); err != nil { 190 return txf, err 191 } 192 193 initNum, initSeq := txf.accountNumber, txf.sequence 194 if initNum == 0 || initSeq == 0 { 195 num, seq, err := txf.accountRetriever.GetAccountNumberSequence(clientCtx, from) 196 if err != nil { 197 return txf, err 198 } 199 200 if initNum == 0 { 201 txf = txf.WithAccountNumber(num) 202 } 203 204 if initSeq == 0 { 205 txf = txf.WithSequence(seq) 206 } 207 } 208 209 return txf, nil 210 } 211 212 // SignWithPrivKey signs a given tx with the given private key, and returns the 213 // corresponding SignatureV2 if the signing is successful. 214 func SignWithPrivKey( 215 signMode signing.SignMode, signerData authsigning.SignerData, 216 txBuilder client.TxBuilder, priv cryptotypes.PrivKey, txConfig client.TxConfig, 217 accSeq uint64, 218 ) (signing.SignatureV2, error) { 219 var sigV2 signing.SignatureV2 220 221 // Generate the bytes to be signed. 222 signBytes, err := txConfig.SignModeHandler().GetSignBytes(signMode, signerData, txBuilder.GetTx()) 223 if err != nil { 224 return sigV2, err 225 } 226 227 // Sign those bytes 228 signature, err := priv.Sign(signBytes) 229 if err != nil { 230 return sigV2, err 231 } 232 233 // Construct the SignatureV2 struct 234 sigData := signing.SingleSignatureData{ 235 SignMode: signMode, 236 Signature: signature, 237 } 238 239 sigV2 = signing.SignatureV2{ 240 PubKey: priv.PubKey(), 241 Data: &sigData, 242 Sequence: accSeq, 243 } 244 245 return sigV2, nil 246 } 247 248 // countDirectSigners counts the number of DIRECT signers in a signature data. 249 func countDirectSigners(data signing.SignatureData) int { 250 switch data := data.(type) { 251 case *signing.SingleSignatureData: 252 if data.SignMode == signing.SignMode_SIGN_MODE_DIRECT { 253 return 1 254 } 255 256 return 0 257 case *signing.MultiSignatureData: 258 directSigners := 0 259 for _, d := range data.Signatures { 260 directSigners += countDirectSigners(d) 261 } 262 263 return directSigners 264 default: 265 panic("unreachable case") 266 } 267 } 268 269 // checkMultipleSigners checks that there can be maximum one DIRECT signer in a tx. 270 func checkMultipleSigners(tx authsigning.Tx) error { 271 directSigners := 0 272 sigsV2, err := tx.GetSignaturesV2() 273 if err != nil { 274 return err 275 } 276 for _, sig := range sigsV2 { 277 directSigners += countDirectSigners(sig.Data) 278 if directSigners > 1 { 279 return sdkerrors.ErrNotSupported.Wrap("txs signed with CLI can have maximum 1 DIRECT signer") 280 } 281 } 282 283 return nil 284 } 285 286 // Sign signs a given tx with a named key. The bytes signed over are canconical. 287 // The resulting signature will be added to the transaction builder overwriting the previous 288 // ones if overwrite=true (otherwise, the signature will be appended). 289 // Signing a transaction with mutltiple signers in the DIRECT mode is not supprted and will 290 // return an error. 291 // An error is returned upon failure. 292 func Sign(txf Factory, name string, txBuilder client.TxBuilder, overwriteSig bool) error { 293 if txf.keybase == nil { 294 return errors.New("keybase must be set prior to signing a transaction") 295 } 296 297 signMode := txf.signMode 298 if signMode == signing.SignMode_SIGN_MODE_UNSPECIFIED { 299 // use the SignModeHandler's default mode if unspecified 300 signMode = txf.txConfig.SignModeHandler().DefaultMode() 301 } 302 303 key, err := txf.keybase.Key(name) 304 if err != nil { 305 return err 306 } 307 pubKey := key.GetPubKey() 308 signerData := authsigning.SignerData{ 309 ChainID: txf.chainID, 310 AccountNumber: txf.accountNumber, 311 Sequence: txf.sequence, 312 } 313 314 // For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on 315 // TxBuilder under the hood, and SignerInfos is needed to generated the 316 // sign bytes. This is the reason for setting SetSignatures here, with a 317 // nil signature. 318 // 319 // Note: this line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it 320 // also doesn't affect its generated sign bytes, so for code's simplicity 321 // sake, we put it here. 322 sigData := signing.SingleSignatureData{ 323 SignMode: signMode, 324 Signature: nil, 325 } 326 sig := signing.SignatureV2{ 327 PubKey: pubKey, 328 Data: &sigData, 329 Sequence: txf.Sequence(), 330 } 331 332 var prevSignatures []signing.SignatureV2 333 if !overwriteSig { 334 prevSignatures, err = txBuilder.GetTx().GetSignaturesV2() 335 if err != nil { 336 return err 337 } 338 } 339 // Overwrite or append signer infos. 340 var sigs []signing.SignatureV2 341 if overwriteSig { 342 sigs = []signing.SignatureV2{sig} 343 } else { 344 sigs = append(prevSignatures, sig) 345 } 346 if err := txBuilder.SetSignatures(sigs...); err != nil { 347 return err 348 } 349 350 if err := checkMultipleSigners(txBuilder.GetTx()); err != nil { 351 return err 352 } 353 354 // Generate the bytes to be signed. 355 bytesToSign, err := txf.txConfig.SignModeHandler().GetSignBytes(signMode, signerData, txBuilder.GetTx()) 356 if err != nil { 357 return err 358 } 359 360 // Sign those bytes 361 sigBytes, _, err := txf.keybase.Sign(name, bytesToSign) 362 if err != nil { 363 return err 364 } 365 366 // Construct the SignatureV2 struct 367 sigData = signing.SingleSignatureData{ 368 SignMode: signMode, 369 Signature: sigBytes, 370 } 371 sig = signing.SignatureV2{ 372 PubKey: pubKey, 373 Data: &sigData, 374 Sequence: txf.Sequence(), 375 } 376 377 if overwriteSig { 378 return txBuilder.SetSignatures(sig) 379 } 380 prevSignatures = append(prevSignatures, sig) 381 return txBuilder.SetSignatures(prevSignatures...) 382 } 383 384 // GasEstimateResponse defines a response definition for tx gas estimation. 385 type GasEstimateResponse struct { 386 GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"` 387 } 388 389 func (gr GasEstimateResponse) String() string { 390 return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) 391 }