From 1028283e6fc1bcd8a64b55fd7ca056d9e3296df9 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Fri, 13 Sep 2019 18:26:29 -0400 Subject: [PATCH] Merge PR #5042: Auxiliary Functionality for Genesis Accounts --- x/auth/alias.go | 4 ++- x/auth/exported/exported.go | 15 +++++++++++ x/auth/exported/exported_test.go | 24 +++++++++++++++++ x/auth/genesis.go | 4 +-- x/auth/simulation/genesis.go | 2 +- x/auth/types/genesis.go | 44 +++++++++++++++++++++++++++----- x/auth/types/genesis_test.go | 40 ++++++++++++++++++++++++++--- 7 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 x/auth/exported/exported_test.go diff --git a/x/auth/alias.go b/x/auth/alias.go index bae90af4c6..7fae8d935d 100644 --- a/x/auth/alias.go +++ b/x/auth/alias.go @@ -55,7 +55,7 @@ var ( NewGenesisState = types.NewGenesisState DefaultGenesisState = types.DefaultGenesisState ValidateGenesis = types.ValidateGenesis - Sanitize = types.Sanitize + SanitizeGenesisAccounts = types.SanitizeGenesisAccounts AddressStoreKey = types.AddressStoreKey NewParams = types.NewParams ParamKeyTable = types.ParamKeyTable @@ -70,6 +70,7 @@ var ( NewTxBuilder = types.NewTxBuilder NewTxBuilderFromCLI = types.NewTxBuilderFromCLI MakeSignature = types.MakeSignature + GetGenesisStateFromAppState = types.GetGenesisStateFromAppState // variable aliases ModuleCdc = types.ModuleCdc @@ -102,4 +103,5 @@ type ( StdSignDoc = types.StdSignDoc StdSignature = types.StdSignature TxBuilder = types.TxBuilder + GenesisAccountIterator = types.GenesisAccountIterator ) diff --git a/x/auth/exported/exported.go b/x/auth/exported/exported.go index 16e66136bf..f48ce48662 100644 --- a/x/auth/exported/exported.go +++ b/x/auth/exported/exported.go @@ -58,6 +58,21 @@ type VestingAccount interface { GetDelegatedVesting() sdk.Coins } +// GenesisAccounts defines a slice of GenesisAccount objects +type GenesisAccounts []GenesisAccount + +// Contains returns true if the given address exists in a slice of GenesisAccount +// objects. +func (ga GenesisAccounts) Contains(addr sdk.AccAddress) bool { + for _, acc := range ga { + if acc.GetAddress().Equals(addr) { + return true + } + } + + return false +} + // GenesisAccount defines a genesis account that embeds an Account with validation capabilities. type GenesisAccount interface { Account diff --git a/x/auth/exported/exported_test.go b/x/auth/exported/exported_test.go new file mode 100644 index 0000000000..8eaab048a8 --- /dev/null +++ b/x/auth/exported/exported_test.go @@ -0,0 +1,24 @@ +package exported_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func TestGenesisAccountsContains(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + acc := authtypes.NewBaseAccount(addr, nil, secp256k1.GenPrivKey().PubKey(), 0, 0) + + genAccounts := exported.GenesisAccounts{} + require.False(t, genAccounts.Contains(acc.GetAddress())) + + genAccounts = append(genAccounts, acc) + require.True(t, genAccounts.Contains(acc.GetAddress())) +} diff --git a/x/auth/genesis.go b/x/auth/genesis.go index f83570e458..22db3557d7 100644 --- a/x/auth/genesis.go +++ b/x/auth/genesis.go @@ -11,7 +11,7 @@ import ( // a genesis port script to the new fee collector account func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { ak.SetParams(ctx, data.Params) - data.Accounts = Sanitize(data.Accounts) + data.Accounts = SanitizeGenesisAccounts(data.Accounts) for _, a := range data.Accounts { acc := ak.NewAccount(ctx, a) @@ -23,7 +23,7 @@ func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState { params := ak.GetParams(ctx) - var genAccounts []exported.GenesisAccount + var genAccounts exported.GenesisAccounts ak.IterateAccounts(ctx, func(account exported.Account) bool { genAccount := account.(exported.GenesisAccount) genAccounts = append(genAccounts, genAccount) diff --git a/x/auth/simulation/genesis.go b/x/auth/simulation/genesis.go index 5f922cec9c..554d031a0d 100644 --- a/x/auth/simulation/genesis.go +++ b/x/auth/simulation/genesis.go @@ -91,7 +91,7 @@ func RandomizedGenState(simState *module.SimulationState) { } // RandomGenesisAccounts returns randomly generated genesis accounts -func RandomGenesisAccounts(simState *module.SimulationState) (genesisAccs []exported.GenesisAccount) { +func RandomGenesisAccounts(simState *module.SimulationState) (genesisAccs exported.GenesisAccounts) { for i, acc := range simState.Accounts { coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))} bacc := types.NewBaseAccountWithAddress(acc.Address) diff --git a/x/auth/types/genesis.go b/x/auth/types/genesis.go index 0d2b620fc3..297666813b 100644 --- a/x/auth/types/genesis.go +++ b/x/auth/types/genesis.go @@ -1,20 +1,22 @@ package types import ( + "encoding/json" "fmt" "sort" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth/exported" ) // GenesisState - all auth state that must be provided at genesis type GenesisState struct { - Params Params `json:"params" yaml:"params"` - Accounts []exported.GenesisAccount `json:"accounts" yaml:"accounts"` + Params Params `json:"params" yaml:"params"` + Accounts exported.GenesisAccounts `json:"accounts" yaml:"accounts"` } // NewGenesisState - Create a new genesis state -func NewGenesisState(params Params, accounts []exported.GenesisAccount) GenesisState { +func NewGenesisState(params Params, accounts exported.GenesisAccounts) GenesisState { return GenesisState{ Params: params, Accounts: accounts, @@ -23,7 +25,18 @@ func NewGenesisState(params Params, accounts []exported.GenesisAccount) GenesisS // DefaultGenesisState - Return a default genesis state func DefaultGenesisState() GenesisState { - return NewGenesisState(DefaultParams(), []exported.GenesisAccount{}) + return NewGenesisState(DefaultParams(), exported.GenesisAccounts{}) +} + +// GetGenesisStateFromAppState returns x/auth GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc *codec.Codec, appState map[string]json.RawMessage) GenesisState { + var genesisState GenesisState + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return genesisState } // ValidateGenesis performs basic validation of auth genesis data returning an @@ -36,8 +49,8 @@ func ValidateGenesis(data GenesisState) error { return validateGenAccounts(data.Accounts) } -// Sanitize sorts accounts and coin sets. -func Sanitize(genAccs []exported.GenesisAccount) []exported.GenesisAccount { +// SanitizeGenesisAccounts sorts accounts and coin sets. +func SanitizeGenesisAccounts(genAccs exported.GenesisAccounts) exported.GenesisAccounts { sort.Slice(genAccs, func(i, j int) bool { return genAccs[i].GetAccountNumber() < genAccs[j].GetAccountNumber() }) @@ -51,7 +64,7 @@ func Sanitize(genAccs []exported.GenesisAccount) []exported.GenesisAccount { return genAccs } -func validateGenAccounts(accounts []exported.GenesisAccount) error { +func validateGenAccounts(accounts exported.GenesisAccounts) error { addrMap := make(map[string]bool, len(accounts)) for _, acc := range accounts { @@ -70,3 +83,20 @@ func validateGenAccounts(accounts []exported.GenesisAccount) error { } return nil } + +// GenesisAccountIterator implements genesis account iteration. +type GenesisAccountIterator struct{} + +// IterateGenesisAccounts iterates over all the genesis accounts found in +// appGenesis and invokes a callback on each genesis account. If any call +// returns true, iteration stops. +func (GenesisAccountIterator) IterateGenesisAccounts( + cdc *codec.Codec, appGenesis map[string]json.RawMessage, cb func(exported.Account) (stop bool), +) { + + for _, genAcc := range GetGenesisStateFromAppState(cdc, appGenesis).Accounts { + if cb(genAcc) { + break + } + } +} diff --git a/x/auth/types/genesis_test.go b/x/auth/types/genesis_test.go index 1dff20bc0f..7656fdbd7d 100644 --- a/x/auth/types/genesis_test.go +++ b/x/auth/types/genesis_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -26,13 +27,13 @@ func TestSanitize(t *testing.T) { sdk.NewInt64Coin("bcoin", 150), }) - genAccs := []exported.GenesisAccount{&authAcc1, &authAcc2} + genAccs := exported.GenesisAccounts{&authAcc1, &authAcc2} require.True(t, genAccs[0].GetAccountNumber() > genAccs[1].GetAccountNumber()) require.Equal(t, genAccs[0].GetCoins()[0].Denom, "bcoin") require.Equal(t, genAccs[0].GetCoins()[1].Denom, "acoin") require.Equal(t, genAccs[1].GetAddress(), addr2) - genAccs = Sanitize(genAccs) + genAccs = SanitizeGenesisAccounts(genAccs) require.False(t, genAccs[0].GetAccountNumber() > genAccs[1].GetAccountNumber()) require.Equal(t, genAccs[1].GetAddress(), addr1) @@ -52,7 +53,7 @@ func TestValidateGenesisDuplicateAccounts(t *testing.T) { acc1 := NewBaseAccountWithAddress(sdk.AccAddress(addr1)) acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) - genAccs := make([]exported.GenesisAccount, 2) + genAccs := make(exported.GenesisAccounts, 2) genAccs[0] = &acc1 genAccs[1] = &acc1 @@ -68,7 +69,7 @@ func TestValidateGenesisInvalidAccounts(t *testing.T) { acc2 := NewBaseAccountWithAddress(sdk.AccAddress(addr2)) acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) - genAccs := make([]exported.GenesisAccount, 2) + genAccs := make(exported.GenesisAccounts, 2) genAccs[0] = baseVestingAcc genAccs[1] = &acc2 @@ -80,3 +81,34 @@ func TestValidateGenesisInvalidAccounts(t *testing.T) { genAccs[0] = NewContinuousVestingAccountRaw(baseVestingAcc, 1548888000) require.Error(t, validateGenAccounts(genAccs)) } + +func TestGenesisAccountIterator(t *testing.T) { + acc1 := NewBaseAccountWithAddress(sdk.AccAddress(addr1)) + acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + + acc2 := NewBaseAccountWithAddress(sdk.AccAddress(addr2)) + acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + + genAccounts := exported.GenesisAccounts{&acc1, &acc2} + + authGenState := DefaultGenesisState() + authGenState.Accounts = genAccounts + + appGenesis := make(map[string]json.RawMessage) + authGenStateBz, err := ModuleCdc.MarshalJSON(authGenState) + require.NoError(t, err) + + appGenesis[ModuleName] = authGenStateBz + + var addresses []sdk.AccAddress + GenesisAccountIterator{}.IterateGenesisAccounts( + ModuleCdc, appGenesis, func(acc exported.Account) (stop bool) { + addresses = append(addresses, acc.GetAddress()) + return false + }, + ) + + require.Len(t, addresses, 2) + require.Equal(t, addresses[0], acc1.GetAddress()) + require.Equal(t, addresses[1], acc2.GetAddress()) +}