github.com/Finschia/finschia-sdk@v0.48.1/simapp/simd/cmd/genaccounts.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/spf13/cobra"
    10  
    11  	"github.com/Finschia/finschia-sdk/client"
    12  	"github.com/Finschia/finschia-sdk/client/flags"
    13  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    14  	"github.com/Finschia/finschia-sdk/server"
    15  	sdk "github.com/Finschia/finschia-sdk/types"
    16  	authtypes "github.com/Finschia/finschia-sdk/x/auth/types"
    17  	authvesting "github.com/Finschia/finschia-sdk/x/auth/vesting/types"
    18  	banktypes "github.com/Finschia/finschia-sdk/x/bank/types"
    19  	"github.com/Finschia/finschia-sdk/x/genutil"
    20  	genutiltypes "github.com/Finschia/finschia-sdk/x/genutil/types"
    21  )
    22  
    23  const (
    24  	flagVestingStart = "vesting-start-time"
    25  	flagVestingEnd   = "vesting-end-time"
    26  	flagVestingAmt   = "vesting-amount"
    27  )
    28  
    29  // AddGenesisAccountCmd returns add-genesis-account cobra Command.
    30  func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command {
    31  	cmd := &cobra.Command{
    32  		Use:   "add-genesis-account [address_or_key_name] [coin][,[coin]]",
    33  		Short: "Add a genesis account to genesis.json",
    34  		Long: `Add a genesis account to genesis.json. The provided account must specify
    35  the account address or key name and a list of initial coins. If a key name is given,
    36  the address will be looked up in the local Keybase. The list of initial tokens must
    37  contain valid denominations. Accounts may optionally be supplied with vesting parameters.
    38  `,
    39  		Args: cobra.ExactArgs(2),
    40  		RunE: func(cmd *cobra.Command, args []string) error {
    41  			clientCtx := client.GetClientContextFromCmd(cmd)
    42  			serverCtx := server.GetServerContextFromCmd(cmd)
    43  			config := serverCtx.Config
    44  
    45  			config.SetRoot(clientCtx.HomeDir)
    46  
    47  			var kb keyring.Keyring
    48  			addr, err := sdk.AccAddressFromBech32(args[0])
    49  			if err != nil {
    50  				inBuf := bufio.NewReader(cmd.InOrStdin())
    51  				keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend)
    52  				if keyringBackend != "" && clientCtx.Keyring == nil {
    53  					var err error
    54  					// attempt to lookup address from Keybase if no address was provided
    55  					kb, err = keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf)
    56  					if err != nil {
    57  						return err
    58  					}
    59  				} else {
    60  					kb = clientCtx.Keyring
    61  				}
    62  
    63  				info, err := kb.Key(args[0])
    64  				if err != nil {
    65  					return fmt.Errorf("failed to get address from Keyring: %w", err)
    66  				}
    67  				addr = info.GetAddress()
    68  			}
    69  
    70  			coins, err := sdk.ParseCoinsNormalized(args[1])
    71  			if err != nil {
    72  				return fmt.Errorf("failed to parse coins: %w", err)
    73  			}
    74  
    75  			vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart)
    76  			vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd)
    77  			vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt)
    78  
    79  			vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr)
    80  			if err != nil {
    81  				return fmt.Errorf("failed to parse vesting amount: %w", err)
    82  			}
    83  
    84  			// create concrete account type based on input parameters
    85  			var genAccount authtypes.GenesisAccount
    86  
    87  			balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}
    88  			baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0)
    89  
    90  			if !vestingAmt.IsZero() {
    91  				baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd)
    92  
    93  				if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) ||
    94  					baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) {
    95  					return errors.New("vesting amount cannot be greater than total amount")
    96  				}
    97  
    98  				switch {
    99  				case vestingStart != 0 && vestingEnd != 0:
   100  					genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart)
   101  
   102  				case vestingEnd != 0:
   103  					genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount)
   104  
   105  				default:
   106  					return errors.New("invalid vesting parameters; must supply start and end time or end time")
   107  				}
   108  			} else {
   109  				genAccount = baseAccount
   110  			}
   111  
   112  			if err := genAccount.Validate(); err != nil {
   113  				return fmt.Errorf("failed to validate new genesis account: %w", err)
   114  			}
   115  
   116  			genFile := config.GenesisFile()
   117  			appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile)
   118  			if err != nil {
   119  				return fmt.Errorf("failed to unmarshal genesis state: %w", err)
   120  			}
   121  
   122  			authGenState := authtypes.GetGenesisStateFromAppState(clientCtx.Codec, appState)
   123  
   124  			accs, err := authtypes.UnpackAccounts(authGenState.Accounts)
   125  			if err != nil {
   126  				return fmt.Errorf("failed to get accounts from any: %w", err)
   127  			}
   128  
   129  			if accs.Contains(addr) {
   130  				return fmt.Errorf("cannot add account at existing address %s", addr)
   131  			}
   132  
   133  			// Add the new account to the set of genesis accounts and sanitize the
   134  			// accounts afterwards.
   135  			accs = append(accs, genAccount)
   136  			accs = authtypes.SanitizeGenesisAccounts(accs)
   137  
   138  			genAccs, err := authtypes.PackAccounts(accs)
   139  			if err != nil {
   140  				return fmt.Errorf("failed to convert accounts into any's: %w", err)
   141  			}
   142  			authGenState.Accounts = genAccs
   143  
   144  			authGenStateBz, err := clientCtx.Codec.MarshalJSON(&authGenState)
   145  			if err != nil {
   146  				return fmt.Errorf("failed to marshal auth genesis state: %w", err)
   147  			}
   148  
   149  			appState[authtypes.ModuleName] = authGenStateBz
   150  
   151  			bankGenState := banktypes.GetGenesisStateFromAppState(clientCtx.Codec, appState)
   152  			bankGenState.Balances = append(bankGenState.Balances, balances)
   153  			bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances)
   154  			bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...)
   155  
   156  			bankGenStateBz, err := clientCtx.Codec.MarshalJSON(bankGenState)
   157  			if err != nil {
   158  				return fmt.Errorf("failed to marshal bank genesis state: %w", err)
   159  			}
   160  
   161  			appState[banktypes.ModuleName] = bankGenStateBz
   162  
   163  			appStateJSON, err := json.Marshal(appState)
   164  			if err != nil {
   165  				return fmt.Errorf("failed to marshal application genesis state: %w", err)
   166  			}
   167  
   168  			genDoc.AppState = appStateJSON
   169  			return genutil.ExportGenesisFile(genDoc, genFile)
   170  		},
   171  	}
   172  
   173  	cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
   174  	cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
   175  	cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
   176  	cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
   177  	cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
   178  	flags.AddQueryFlagsToCmd(cmd)
   179  
   180  	return cmd
   181  }