github.com/Finschia/finschia-sdk@v0.48.1/x/genutil/client/cli/gentx.go (about) 1 package cli 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 12 ostos "github.com/Finschia/ostracon/libs/os" 13 octypes "github.com/Finschia/ostracon/types" 14 "github.com/pkg/errors" 15 "github.com/spf13/cobra" 16 17 "github.com/Finschia/finschia-sdk/client" 18 "github.com/Finschia/finschia-sdk/client/flags" 19 "github.com/Finschia/finschia-sdk/client/tx" 20 "github.com/Finschia/finschia-sdk/crypto/keyring" 21 "github.com/Finschia/finschia-sdk/server" 22 sdk "github.com/Finschia/finschia-sdk/types" 23 "github.com/Finschia/finschia-sdk/types/module" 24 "github.com/Finschia/finschia-sdk/version" 25 authclient "github.com/Finschia/finschia-sdk/x/auth/client" 26 "github.com/Finschia/finschia-sdk/x/genutil" 27 "github.com/Finschia/finschia-sdk/x/genutil/types" 28 "github.com/Finschia/finschia-sdk/x/staking/client/cli" 29 ) 30 31 // GenTxCmd builds the application's gentx command. 32 func GenTxCmd(mbm module.BasicManager, txEncCfg client.TxEncodingConfig, genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command { 33 ipDefault, _ := server.ExternalIP() 34 fsCreateValidator, defaultsDesc := cli.CreateValidatorMsgFlagSet(ipDefault) 35 36 cmd := &cobra.Command{ 37 Use: "gentx [key_name] [amount]", 38 Short: "Generate a genesis tx carrying a self delegation", 39 Args: cobra.ExactArgs(2), 40 Long: fmt.Sprintf(`Generate a genesis transaction that creates a validator with a self-delegation, 41 that is signed by the key in the Keyring referenced by a given name. A node ID and Bech32 consensus 42 pubkey may optionally be provided. If they are omitted, they will be retrieved from the priv_validator.json 43 file. The following default parameters are included: 44 %s 45 46 Example: 47 $ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=os --chain-id=test-chain-1 \ 48 --moniker="myvalidator" \ 49 --commission-max-change-rate=0.01 \ 50 --commission-max-rate=1.0 \ 51 --commission-rate=0.07 \ 52 --details="..." \ 53 --security-contact="..." \ 54 --website="..." 55 `, defaultsDesc, version.AppName, 56 ), 57 RunE: func(cmd *cobra.Command, args []string) error { 58 serverCtx := server.GetServerContextFromCmd(cmd) 59 clientCtx, err := client.GetClientQueryContext(cmd) 60 if err != nil { 61 return err 62 } 63 cdc := clientCtx.Codec 64 65 config := serverCtx.Config 66 config.SetRoot(clientCtx.HomeDir) 67 68 nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(serverCtx.Config) 69 if err != nil { 70 return errors.Wrap(err, "failed to initialize node validator files") 71 } 72 73 // read --nodeID, if empty take it from priv_validator.json 74 if nodeIDString, _ := cmd.Flags().GetString(cli.FlagNodeID); nodeIDString != "" { 75 nodeID = nodeIDString 76 } 77 78 // read --pubkey, if empty take it from priv_validator.json 79 if pkStr, _ := cmd.Flags().GetString(cli.FlagPubKey); pkStr != "" { 80 if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &valPubKey); err != nil { 81 return errors.Wrap(err, "failed to unmarshal validator public key") 82 } 83 } 84 85 genDoc, err := octypes.GenesisDocFromFile(config.GenesisFile()) 86 if err != nil { 87 return errors.Wrapf(err, "failed to read genesis doc file %s", config.GenesisFile()) 88 } 89 90 var genesisState map[string]json.RawMessage 91 if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { 92 return errors.Wrap(err, "failed to unmarshal genesis state") 93 } 94 95 if err = mbm.ValidateGenesis(cdc, txEncCfg, genesisState); err != nil { 96 return errors.Wrap(err, "failed to validate genesis state") 97 } 98 99 inBuf := bufio.NewReader(cmd.InOrStdin()) 100 101 name := args[0] 102 key, err := clientCtx.Keyring.Key(name) 103 if err != nil { 104 return errors.Wrapf(err, "failed to fetch '%s' from the keyring", name) 105 } 106 107 moniker := config.Moniker 108 if m, _ := cmd.Flags().GetString(cli.FlagMoniker); m != "" { 109 moniker = m 110 } 111 112 // set flags for creating a gentx 113 createValCfg, err := cli.PrepareConfigForTxCreateValidator(cmd.Flags(), moniker, nodeID, genDoc.ChainID, valPubKey) 114 if err != nil { 115 return errors.Wrap(err, "error creating configuration to create validator msg") 116 } 117 118 amount := args[1] 119 coins, err := sdk.ParseCoinsNormalized(amount) 120 if err != nil { 121 return errors.Wrap(err, "failed to parse coins") 122 } 123 124 err = genutil.ValidateAccountInGenesis(genesisState, genBalIterator, key.GetAddress(), coins, cdc) 125 if err != nil { 126 return errors.Wrap(err, "failed to validate account in genesis") 127 } 128 129 txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) 130 if err != nil { 131 return errors.Wrap(err, "error creating tx builder") 132 } 133 134 clientCtx = clientCtx.WithInput(inBuf).WithFromAddress(key.GetAddress()) 135 136 // The following line comes from a discrepancy between the `gentx` 137 // and `create-validator` commands: 138 // - `gentx` expects amount as an arg, 139 // - `create-validator` expects amount as a required flag. 140 // ref: https://github.com/cosmos/cosmos-sdk/issues/8251 141 // Since gentx doesn't set the amount flag (which `create-validator` 142 // reads from), we copy the amount arg into the valCfg directly. 143 // 144 // Ideally, the `create-validator` command should take a validator 145 // config file instead of so many flags. 146 // ref: https://github.com/cosmos/cosmos-sdk/issues/8177 147 createValCfg.Amount = amount 148 149 // create a 'create-validator' message 150 txBldr, msg, err := cli.BuildCreateValidatorMsg(clientCtx, createValCfg, txFactory, true) 151 if err != nil { 152 return errors.Wrap(err, "failed to build create-validator message") 153 } 154 155 if key.GetType() == keyring.TypeOffline || key.GetType() == keyring.TypeMulti { 156 cmd.PrintErrln("Offline key passed in. Use `tx sign` command to sign.") 157 return authclient.PrintUnsignedStdTx(txBldr, clientCtx, []sdk.Msg{msg}) 158 } 159 160 // write the unsigned transaction to the buffer 161 w := bytes.NewBuffer([]byte{}) 162 clientCtx = clientCtx.WithOutput(w) 163 164 if err = msg.ValidateBasic(); err != nil { 165 return err 166 } 167 168 if err = authclient.PrintUnsignedStdTx(txBldr, clientCtx, []sdk.Msg{msg}); err != nil { 169 return errors.Wrap(err, "failed to print unsigned std tx") 170 } 171 172 // read the transaction 173 stdTx, err := readUnsignedGenTxFile(clientCtx, w) 174 if err != nil { 175 return errors.Wrap(err, "failed to read unsigned gen tx file") 176 } 177 178 // sign the transaction and write it to the output file 179 txBuilder, err := clientCtx.TxConfig.WrapTxBuilder(stdTx) 180 if err != nil { 181 return fmt.Errorf("error creating tx builder: %w", err) 182 } 183 184 err = authclient.SignTx(txFactory, clientCtx, name, txBuilder, true, true) 185 if err != nil { 186 return errors.Wrap(err, "failed to sign std tx") 187 } 188 189 outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument) 190 if outputDocument == "" { 191 outputDocument, err = makeOutputFilepath(config.RootDir, nodeID) 192 if err != nil { 193 return errors.Wrap(err, "failed to create output file path") 194 } 195 } 196 197 if err := writeSignedGenTx(clientCtx, outputDocument, stdTx); err != nil { 198 return errors.Wrap(err, "failed to write signed gen tx") 199 } 200 201 cmd.PrintErrf("Genesis transaction written to %q\n", outputDocument) 202 return nil 203 }, 204 } 205 206 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 207 cmd.Flags().String(flags.FlagOutputDocument, "", "Write the genesis transaction JSON document to the given file instead of the default location") 208 cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") 209 cmd.Flags().AddFlagSet(fsCreateValidator) 210 flags.AddTxFlagsToCmd(cmd) 211 212 return cmd 213 } 214 215 func makeOutputFilepath(rootDir, nodeID string) (string, error) { 216 writePath := filepath.Join(rootDir, "config", "gentx") 217 if err := ostos.EnsureDir(writePath, 0700); err != nil { 218 return "", err 219 } 220 221 return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil 222 } 223 224 func readUnsignedGenTxFile(clientCtx client.Context, r io.Reader) (sdk.Tx, error) { 225 bz, err := io.ReadAll(r) 226 if err != nil { 227 return nil, err 228 } 229 230 aTx, err := clientCtx.TxConfig.TxJSONDecoder()(bz) 231 if err != nil { 232 return nil, err 233 } 234 235 return aTx, err 236 } 237 238 func writeSignedGenTx(clientCtx client.Context, outputDocument string, tx sdk.Tx) error { 239 outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o644) 240 if err != nil { 241 return err 242 } 243 defer outputFile.Close() 244 245 json, err := clientCtx.TxConfig.TxJSONEncoder()(tx) 246 if err != nil { 247 return err 248 } 249 250 _, err = fmt.Fprintf(outputFile, "%s\n", json) 251 252 return err 253 }