github.com/cosmos/cosmos-sdk@v0.50.10/x/genutil/genaccounts.go (about) 1 package genutil 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 8 "cosmossdk.io/core/address" 9 10 "github.com/cosmos/cosmos-sdk/codec" 11 sdk "github.com/cosmos/cosmos-sdk/types" 12 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 13 authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" 14 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 15 genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" 16 ) 17 18 // AddGenesisAccount adds a genesis account to the genesis state. 19 // Where `cdc` is client codec, `genesisFileUrl` is the path/url of current genesis file, 20 // `accAddr` is the address to be added to the genesis state, `amountStr` is the list of initial coins 21 // to be added for the account, `appendAcct` updates the account if already exists. 22 // `vestingStart, vestingEnd and vestingAmtStr` respectively are the schedule start time, end time (unix epoch) 23 // `moduleNameā is the module name for which the account is being created 24 // and coins to be appended to the account already in the genesis.json file. 25 func AddGenesisAccount( 26 cdc codec.Codec, 27 accAddr sdk.AccAddress, 28 appendAcct bool, 29 genesisFileURL, amountStr, vestingAmtStr string, 30 vestingStart, vestingEnd int64, 31 moduleName string, 32 ) error { 33 coins, err := sdk.ParseCoinsNormalized(amountStr) 34 if err != nil { 35 return fmt.Errorf("failed to parse coins: %w", err) 36 } 37 38 vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) 39 if err != nil { 40 return fmt.Errorf("failed to parse vesting amount: %w", err) 41 } 42 43 // create concrete account type based on input parameters 44 var genAccount authtypes.GenesisAccount 45 46 balances := banktypes.Balance{Address: accAddr.String(), Coins: coins.Sort()} 47 baseAccount := authtypes.NewBaseAccount(accAddr, nil, 0, 0) 48 49 if !vestingAmt.IsZero() { 50 baseVestingAccount, err := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) 51 if err != nil { 52 return fmt.Errorf("failed to create base vesting account: %w", err) 53 } 54 55 if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || 56 baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { 57 return errors.New("vesting amount cannot be greater than total amount") 58 } 59 60 switch { 61 case vestingStart != 0 && vestingEnd != 0: 62 genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) 63 64 case vestingEnd != 0: 65 genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) 66 67 default: 68 return errors.New("invalid vesting parameters; must supply start and end time or end time") 69 } 70 } else if moduleName != "" { 71 genAccount = authtypes.NewEmptyModuleAccount(moduleName, authtypes.Burner, authtypes.Minter) 72 } else { 73 genAccount = baseAccount 74 } 75 76 if err := genAccount.Validate(); err != nil { 77 return fmt.Errorf("failed to validate new genesis account: %w", err) 78 } 79 80 appState, appGenesis, err := genutiltypes.GenesisStateFromGenFile(genesisFileURL) 81 if err != nil { 82 return fmt.Errorf("failed to unmarshal genesis state: %w", err) 83 } 84 85 authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) 86 87 accs, err := authtypes.UnpackAccounts(authGenState.Accounts) 88 if err != nil { 89 return fmt.Errorf("failed to get accounts from any: %w", err) 90 } 91 92 bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) 93 if accs.Contains(accAddr) { 94 if !appendAcct { 95 return fmt.Errorf(" Account %s already exists\nUse `append` flag to append account at existing address", accAddr) 96 } 97 98 genesisB := banktypes.GetGenesisStateFromAppState(cdc, appState) 99 for idx, acc := range genesisB.Balances { 100 if acc.Address != accAddr.String() { 101 continue 102 } 103 104 updatedCoins := acc.Coins.Add(coins...) 105 bankGenState.Balances[idx] = banktypes.Balance{Address: accAddr.String(), Coins: updatedCoins.Sort()} 106 break 107 } 108 } else { 109 // Add the new account to the set of genesis accounts and sanitize the accounts afterwards. 110 accs = append(accs, genAccount) 111 accs = authtypes.SanitizeGenesisAccounts(accs) 112 113 genAccs, err := authtypes.PackAccounts(accs) 114 if err != nil { 115 return fmt.Errorf("failed to convert accounts into any's: %w", err) 116 } 117 authGenState.Accounts = genAccs 118 119 authGenStateBz, err := cdc.MarshalJSON(&authGenState) 120 if err != nil { 121 return fmt.Errorf("failed to marshal auth genesis state: %w", err) 122 } 123 appState[authtypes.ModuleName] = authGenStateBz 124 125 bankGenState.Balances = append(bankGenState.Balances, balances) 126 } 127 128 bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) 129 130 bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...) 131 132 bankGenStateBz, err := cdc.MarshalJSON(bankGenState) 133 if err != nil { 134 return fmt.Errorf("failed to marshal bank genesis state: %w", err) 135 } 136 appState[banktypes.ModuleName] = bankGenStateBz 137 138 appStateJSON, err := json.Marshal(appState) 139 if err != nil { 140 return fmt.Errorf("failed to marshal application genesis state: %w", err) 141 } 142 143 appGenesis.AppState = appStateJSON 144 return ExportGenesisFile(appGenesis, genesisFileURL) 145 } 146 147 type GenesisAccount struct { 148 // Base 149 Address string `json:"address"` 150 Coins sdk.Coins `json:"coins"` 151 152 // Vesting 153 VestingAmt sdk.Coins `json:"vesting_amt,omitempty"` 154 VestingStart int64 `json:"vesting_start,omitempty"` 155 VestingEnd int64 `json:"vesting_end,omitempty"` 156 157 // Module 158 ModuleName string `json:"module_name,omitempty"` 159 } 160 161 // AddGenesisAccounts adds genesis accounts to the genesis state. 162 // Where `cdc` is the client codec, `accounts` are the genesis accounts to add, 163 // `appendAcct` updates the account if already exists, and `genesisFileURL` is the path/url of the current genesis file. 164 func AddGenesisAccounts( 165 cdc codec.Codec, 166 ac address.Codec, 167 accounts []GenesisAccount, 168 appendAcct bool, 169 genesisFileURL string, 170 ) error { 171 appState, appGenesis, err := genutiltypes.GenesisStateFromGenFile(genesisFileURL) 172 if err != nil { 173 return fmt.Errorf("failed to unmarshal genesis state: %w", err) 174 } 175 176 authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) 177 bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) 178 179 accs, err := authtypes.UnpackAccounts(authGenState.Accounts) 180 if err != nil { 181 return fmt.Errorf("failed to get accounts from any: %w", err) 182 } 183 184 newSupplyCoinsCache := sdk.NewCoins() 185 balanceCache := make(map[string]banktypes.Balance) 186 for _, acc := range accs { 187 for _, balance := range bankGenState.GetBalances() { 188 if balance.Address == acc.GetAddress().String() { 189 balanceCache[acc.GetAddress().String()] = balance 190 } 191 } 192 } 193 194 for _, acc := range accounts { 195 addr := acc.Address 196 coins := acc.Coins 197 198 accAddr, err := ac.StringToBytes(addr) 199 if err != nil { 200 return fmt.Errorf("failed to parse account address %s: %w", addr, err) 201 } 202 203 // create concrete account type based on input parameters 204 var genAccount authtypes.GenesisAccount 205 206 balances := banktypes.Balance{Address: addr, Coins: coins.Sort()} 207 baseAccount := authtypes.NewBaseAccount(accAddr, nil, 0, 0) 208 209 vestingAmt := acc.VestingAmt 210 if !vestingAmt.IsZero() { 211 vestingStart := acc.VestingStart 212 vestingEnd := acc.VestingEnd 213 214 baseVestingAccount, err := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) 215 if err != nil { 216 return fmt.Errorf("failed to create base vesting account: %w", err) 217 } 218 219 if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || 220 baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { 221 return errors.New("vesting amount cannot be greater than total amount") 222 } 223 224 switch { 225 case vestingStart != 0 && vestingEnd != 0: 226 genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) 227 228 case vestingEnd != 0: 229 genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) 230 231 default: 232 return errors.New("invalid vesting parameters; must supply start and end time or end time") 233 } 234 } else if acc.ModuleName != "" { 235 genAccount = authtypes.NewEmptyModuleAccount(acc.ModuleName, authtypes.Burner, authtypes.Minter) 236 } else { 237 genAccount = baseAccount 238 } 239 240 if err := genAccount.Validate(); err != nil { 241 return fmt.Errorf("failed to validate new genesis account: %w", err) 242 } 243 244 if _, ok := balanceCache[addr]; ok { 245 if !appendAcct { 246 return fmt.Errorf(" Account %s already exists\nUse `append` flag to append account at existing address", accAddr) 247 } 248 249 for idx, acc := range bankGenState.Balances { 250 if acc.Address != addr { 251 continue 252 } 253 254 updatedCoins := acc.Coins.Add(coins...) 255 bankGenState.Balances[idx] = banktypes.Balance{Address: addr, Coins: updatedCoins.Sort()} 256 break 257 } 258 } else { 259 accs = append(accs, genAccount) 260 bankGenState.Balances = append(bankGenState.Balances, balances) 261 } 262 263 newSupplyCoinsCache = newSupplyCoinsCache.Add(coins...) 264 } 265 266 accs = authtypes.SanitizeGenesisAccounts(accs) 267 268 authGenState.Accounts, err = authtypes.PackAccounts(accs) 269 if err != nil { 270 return fmt.Errorf("failed to convert accounts into any's: %w", err) 271 } 272 273 appState[authtypes.ModuleName], err = cdc.MarshalJSON(&authGenState) 274 if err != nil { 275 return fmt.Errorf("failed to marshal auth genesis state: %w", err) 276 } 277 278 bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) 279 bankGenState.Supply = bankGenState.Supply.Add(newSupplyCoinsCache...) 280 281 appState[banktypes.ModuleName], err = cdc.MarshalJSON(bankGenState) 282 if err != nil { 283 return fmt.Errorf("failed to marshal bank genesis state: %w", err) 284 } 285 286 appStateJSON, err := json.Marshal(appState) 287 if err != nil { 288 return fmt.Errorf("failed to marshal application genesis state: %w", err) 289 } 290 291 appGenesis.AppState = appStateJSON 292 return ExportGenesisFile(appGenesis, genesisFileURL) 293 }