github.com/Finschia/finschia-sdk@v0.48.1/x/auth/client/cli/tx_sign.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "os" 6 7 "github.com/spf13/cobra" 8 9 "github.com/Finschia/finschia-sdk/client" 10 "github.com/Finschia/finschia-sdk/client/flags" 11 "github.com/Finschia/finschia-sdk/client/tx" 12 sdk "github.com/Finschia/finschia-sdk/types" 13 authclient "github.com/Finschia/finschia-sdk/x/auth/client" 14 ) 15 16 const ( 17 flagMultisig = "multisig" 18 flagOverwrite = "overwrite" 19 flagSigOnly = "signature-only" 20 flagAmino = "amino" 21 flagNoAutoIncrement = "no-auto-increment" 22 ) 23 24 // GetSignBatchCommand returns the transaction sign-batch command. 25 func GetSignBatchCommand() *cobra.Command { 26 cmd := &cobra.Command{ 27 Use: "sign-batch [file]", 28 Short: "Sign transaction batch files", 29 Long: `Sign batch files of transactions generated with --generate-only. 30 The command processes list of transactions from file (one StdTx each line), generate 31 signed transactions or signatures and print their JSON encoding, delimited by '\n'. 32 As the signatures are generated, the command updates the account sequence number accordingly. 33 34 If the --signature-only flag is set, it will output the signature parts only. 35 36 The --offline flag makes sure that the client will not reach out to full node. 37 As a result, the account and the sequence number queries will not be performed and 38 it is required to set such parameters manually. Note, invalid values will cause 39 the transaction to fail. The sequence will be incremented automatically for each 40 transaction that is signed. 41 42 The --multisig=<multisig_key> flag generates a signature on behalf of a multisig 43 account key. It implies --signature-only. 44 `, 45 PreRun: preSignCmd, 46 RunE: makeSignBatchCmd(), 47 Args: cobra.ExactArgs(1), 48 } 49 50 cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") 51 cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") 52 cmd.Flags().Bool(flagSigOnly, true, "Print only the generated signature, then exit") 53 cmd.Flags().String(flags.FlagChainID, "", "network chain ID") 54 cmd.MarkFlagRequired(flags.FlagFrom) 55 flags.AddTxFlagsToCmd(cmd) 56 57 return cmd 58 } 59 60 func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { 61 return func(cmd *cobra.Command, args []string) error { 62 clientCtx, err := client.GetClientTxContext(cmd) 63 if err != nil { 64 return err 65 } 66 txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) 67 txCfg := clientCtx.TxConfig 68 printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) 69 infile := os.Stdin 70 71 ms, err := cmd.Flags().GetString(flagMultisig) 72 if err != nil { 73 return err 74 } 75 76 // prepare output document 77 closeFunc, err := setOutputFile(cmd) 78 if err != nil { 79 return err 80 } 81 82 defer closeFunc() 83 clientCtx.WithOutput(cmd.OutOrStdout()) 84 85 if args[0] != "-" { 86 infile, err = os.Open(args[0]) 87 if err != nil { 88 return err 89 } 90 } 91 scanner := authclient.NewBatchScanner(txCfg, infile) 92 93 if !clientCtx.Offline { 94 if ms == "" { 95 from, err := cmd.Flags().GetString(flags.FlagFrom) 96 if err != nil { 97 return err 98 } 99 100 addr, _, _, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly) 101 if err != nil { 102 return err 103 } 104 105 acc, err := txFactory.AccountRetriever().GetAccount(clientCtx, addr) 106 if err != nil { 107 return err 108 } 109 110 txFactory = txFactory.WithAccountNumber(acc.GetAccountNumber()).WithSequence(acc.GetSequence()) 111 } else { 112 txFactory = txFactory.WithAccountNumber(0).WithSequence(0) 113 } 114 } 115 116 for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ { 117 unsignedStdTx := scanner.Tx() 118 txFactory = txFactory.WithSequence(sequence) 119 txBuilder, err := txCfg.WrapTxBuilder(unsignedStdTx) 120 if err != nil { 121 return err 122 } 123 if ms == "" { 124 from, _ := cmd.Flags().GetString(flags.FlagFrom) 125 _, fromName, _, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly) 126 if err != nil { 127 return fmt.Errorf("error getting account from keybase: %w", err) 128 } 129 err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true) 130 if err != nil { 131 return err 132 } 133 } else { 134 multisigAddr, _, _, err := client.GetFromFields(txFactory.Keybase(), ms, clientCtx.GenerateOnly) 135 if err != nil { 136 return fmt.Errorf("error getting account from keybase: %w", err) 137 } 138 err = authclient.SignTxWithSignerAddress( 139 txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, true) 140 if err != nil { 141 return err 142 } 143 } 144 145 if err != nil { 146 return err 147 } 148 149 json, err := marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) 150 if err != nil { 151 return err 152 } 153 154 cmd.Printf("%s\n", json) 155 } 156 157 if err := scanner.UnmarshalErr(); err != nil { 158 return err 159 } 160 161 return scanner.UnmarshalErr() 162 } 163 } 164 165 func setOutputFile(cmd *cobra.Command) (func(), error) { 166 outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) 167 if outputDoc == "" { 168 return func() {}, nil 169 } 170 171 fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) 172 if err != nil { 173 return func() {}, err 174 } 175 176 cmd.SetOut(fp) 177 178 return func() { fp.Close() }, nil 179 } 180 181 // GetSignCommand returns the transaction sign command. 182 func GetSignCommand() *cobra.Command { 183 cmd := &cobra.Command{ 184 Use: "sign [file]", 185 Short: "Sign a transaction generated offline", 186 Long: `Sign a transaction created with the --generate-only flag. 187 It will read a transaction from [file], sign it, and print its JSON encoding. 188 189 If the --signature-only flag is set, it will output the signature parts only. 190 191 The --offline flag makes sure that the client will not reach out to full node. 192 As a result, the account and sequence number queries will not be performed and 193 it is required to set such parameters manually. Note, invalid values will cause 194 the transaction to fail. 195 196 The --multisig=<multisig_key> flag generates a signature on behalf of a multisig account 197 key. It implies --signature-only. Full multisig signed transactions may eventually 198 be generated via the 'multisign' command. 199 `, 200 PreRun: preSignCmd, 201 RunE: makeSignCmd(), 202 Args: cobra.ExactArgs(1), 203 } 204 205 cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") 206 cmd.Flags().Bool(flagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") 207 cmd.Flags().Bool(flagSigOnly, false, "Print only the signatures") 208 cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") 209 cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") 210 cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") 211 cmd.MarkFlagRequired(flags.FlagFrom) 212 flags.AddTxFlagsToCmd(cmd) 213 214 return cmd 215 } 216 217 func preSignCmd(cmd *cobra.Command, _ []string) { 218 // Conditionally mark the account and sequence numbers required as no RPC 219 // query will be done. 220 if offline, _ := cmd.Flags().GetBool(flags.FlagOffline); offline { 221 cmd.MarkFlagRequired(flags.FlagAccountNumber) 222 cmd.MarkFlagRequired(flags.FlagSequence) 223 } 224 } 225 226 func makeSignCmd() func(cmd *cobra.Command, args []string) error { 227 return func(cmd *cobra.Command, args []string) (err error) { 228 var clientCtx client.Context 229 230 clientCtx, err = client.GetClientTxContext(cmd) 231 if err != nil { 232 return err 233 } 234 f := cmd.Flags() 235 236 clientCtx, txF, newTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) 237 if err != nil { 238 return err 239 } 240 241 txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) 242 txCfg := clientCtx.TxConfig 243 txBuilder, err := txCfg.WrapTxBuilder(newTx) 244 if err != nil { 245 return err 246 } 247 248 printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) 249 multisig, _ := cmd.Flags().GetString(flagMultisig) 250 if err != nil { 251 return err 252 } 253 from, _ := cmd.Flags().GetString(flags.FlagFrom) 254 _, fromName, _, err := client.GetFromFields(txF.Keybase(), from, clientCtx.GenerateOnly) 255 if err != nil { 256 return fmt.Errorf("error getting account from keybase: %w", err) 257 } 258 259 overwrite, _ := f.GetBool(flagOverwrite) 260 if multisig != "" { 261 multisigAddr, err := sdk.AccAddressFromBech32(multisig) 262 if err != nil { 263 // Bech32 decode error, maybe it's a name, we try to fetch from keyring 264 multisigAddr, _, _, err = client.GetFromFields(txFactory.Keybase(), multisig, clientCtx.GenerateOnly) 265 if err != nil { 266 return fmt.Errorf("error getting account from keybase: %w", err) 267 } 268 } 269 err = authclient.SignTxWithSignerAddress( 270 txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline, overwrite) 271 if err != nil { 272 return err 273 } 274 printSignatureOnly = true 275 } else { 276 err = authclient.SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, overwrite) 277 } 278 if err != nil { 279 return err 280 } 281 282 aminoJSON, err := f.GetBool(flagAmino) 283 if err != nil { 284 return err 285 } 286 287 var json []byte 288 if aminoJSON { 289 stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) 290 if err != nil { 291 return err 292 } 293 req := BroadcastReq{ 294 Tx: stdTx, 295 Mode: "block|sync|async", 296 } 297 json, err = clientCtx.LegacyAmino.MarshalJSON(req) 298 if err != nil { 299 return err 300 } 301 } else { 302 json, err = marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) 303 if err != nil { 304 return err 305 } 306 } 307 308 outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) 309 if outputDoc == "" { 310 cmd.Printf("%s\n", json) 311 return nil 312 } 313 314 fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) 315 if err != nil { 316 return err 317 } 318 defer func() { 319 err2 := fp.Close() 320 if err == nil { 321 err = err2 322 } 323 }() 324 325 _, err = fp.Write(append(json, '\n')) 326 return err 327 } 328 } 329 330 func marshalSignatureJSON(txConfig client.TxConfig, txBldr client.TxBuilder, signatureOnly bool) ([]byte, error) { 331 parsedTx := txBldr.GetTx() 332 if signatureOnly { 333 sigs, err := parsedTx.GetSignaturesV2() 334 if err != nil { 335 return nil, err 336 } 337 return txConfig.MarshalSignatureJSON(sigs) 338 } 339 340 return txConfig.TxJSONEncoder()(parsedTx) 341 }