github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/docs/architecture/adr-011-generalize-genesis-accounts.md (about) 1 # ADR 011: Generalize Genesis Accounts 2 3 ## Changelog 4 5 - 2019-08-30: initial draft 6 7 ## Context 8 9 Currently, the SDK allows for custom account types; the `auth` keeper stores any type fulfilling its `Account` interface. However `auth` does not handle exporting or loading accounts to/from a genesis file, this is done by `genaccounts`, which only handles one of 4 concrete account types (`BaseAccount`, `ContinuousVestingAccount`, `DelayedVestingAccount` and `ModuleAccount`). 10 11 Projects desiring to use custom accounts (say custom vesting accounts) need to fork and modify `genaccounts`. 12 13 ## Decision 14 15 In summary, we will (un)marshal all accounts (interface types) directly using amino, rather than converting to `genaccounts`’s `GenesisAccount` type. Since doing this removes the majority of `genaccounts`'s code, we will merge `genaccounts` into `auth`. Marshalled accounts will be stored in `auth`'s genesis state. 16 17 Detailed changes: 18 19 ### 1) (Un)Marshal accounts directly using amino 20 21 The `auth` module's `GenesisState` gains a new field `Accounts`. Note these aren't of type `exported.Account` for reasons outlined in section 3. 22 23 ```go 24 // GenesisState - all auth state that must be provided at genesis 25 type GenesisState struct { 26 Params Params `json:"params" yaml:"params"` 27 Accounts []GenesisAccount `json:"accounts" yaml:"accounts"` 28 } 29 ``` 30 31 Now `auth`'s `InitGenesis` and `ExportGenesis` (un)marshal accounts as well as the defined params. 32 33 ```go 34 // InitGenesis - Init store state from genesis data 35 func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { 36 ak.SetParams(ctx, data.Params) 37 // load the accounts 38 for _, a := range data.Accounts { 39 acc := ak.NewAccount(ctx, a) // set account number 40 ak.SetAccount(ctx, acc) 41 } 42 } 43 44 // ExportGenesis returns a GenesisState for a given context and keeper 45 func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState { 46 params := ak.GetParams(ctx) 47 48 var genAccounts []exported.GenesisAccount 49 ak.IterateAccounts(ctx, func(account exported.Account) bool { 50 genAccount := account.(exported.GenesisAccount) 51 genAccounts = append(genAccounts, genAccount) 52 return false 53 }) 54 55 return NewGenesisState(params, genAccounts) 56 } 57 ``` 58 59 ### 2) Register custom account types on the `auth` codec 60 61 The `auth` codec must have all custom account types registered to marshal them. We will follow the pattern established in `gov` for proposals. 62 63 An example custom account definition: 64 65 ```go 66 import authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 67 68 // Register the module account type with the auth module codec so it can decode module accounts stored in a genesis file 69 func init() { 70 authtypes.RegisterAccountTypeCodec(ModuleAccount{}, "cosmos-sdk/ModuleAccount") 71 } 72 73 type ModuleAccount struct { 74 ... 75 ``` 76 77 The `auth` codec definition: 78 79 ```go 80 var ModuleCdc *codec.Codec 81 82 func init() { 83 ModuleCdc = codec.New() 84 // register module msg's and Account interface 85 ... 86 // leave the codec unsealed 87 } 88 89 // RegisterAccountTypeCodec registers an external account type defined in another module for the internal ModuleCdc. 90 func RegisterAccountTypeCodec(o interface{}, name string) { 91 ModuleCdc.RegisterConcrete(o, name, nil) 92 } 93 ``` 94 95 ### 3) Genesis validation for custom account types 96 97 Modules implement a `ValidateGenesis` method. As `auth` does not know of account implementations, accounts will need to validate themselves. 98 99 We will unmarshal accounts into a `GenesisAccount` interface that includes a `Validate` method. 100 101 ```go 102 type GenesisAccount interface { 103 exported.Account 104 Validate() error 105 } 106 ``` 107 108 Then the `auth` `ValidateGenesis` function becomes: 109 110 ```go 111 // ValidateGenesis performs basic validation of auth genesis data returning an 112 // error for any failed validation criteria. 113 func ValidateGenesis(data GenesisState) error { 114 // Validate params 115 ... 116 117 // Validate accounts 118 addrMap := make(map[string]bool, len(data.Accounts)) 119 for _, acc := range data.Accounts { 120 121 // check for duplicated accounts 122 addrStr := acc.GetAddress().String() 123 if _, ok := addrMap[addrStr]; ok { 124 return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) 125 } 126 addrMap[addrStr] = true 127 128 // check account specific validation 129 if err := acc.Validate(); err != nil { 130 return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error()) 131 } 132 133 } 134 return nil 135 } 136 ``` 137 138 ### 4) Move add-genesis-account cli to `auth` 139 140 The `genaccounts` module contains a cli command to add base or vesting accounts to a genesis file. 141 142 This will be moved to `auth`. We will leave it to projects to write their own commands to add custom accounts. An extensible cli handler, similar to `gov`, could be created but it is not worth the complexity for this minor use case. 143 144 ### 5) Update module and vesting accounts 145 146 Under the new scheme, module and vesting account types need some minor updates: 147 148 - Type registration on `auth`'s codec (shown above) 149 - A `Validate` method for each `Account` concrete type 150 151 ## Status 152 153 Proposed 154 155 ## Consequences 156 157 ### Positive 158 159 - custom accounts can be used without needing to fork `genaccounts` 160 - reduction in lines of code 161 162 ### Negative 163 164 ### Neutral 165 166 - `genaccounts` module no longer exists 167 - accounts in genesis files are stored under `accounts` in `auth` rather than in the `genaccounts` module. 168 -`add-genesis-account` cli command now in `auth` 169 170 ## References