Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADR 11: Generalize Gen Accounts implementation #5017

Merged
merged 26 commits into from
Sep 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
40ed336
ADR 11: Generalize Gen Accounts implementation
fedekunze Sep 9, 2019
90a3882
move CLI cmd to auth
fedekunze Sep 10, 2019
8e92e02
delete genaccount module
fedekunze Sep 10, 2019
e1e4993
add some tests
fedekunze Sep 10, 2019
84f92dd
more fixes
fedekunze Sep 10, 2019
19fd7c8
register codec
fedekunze Sep 10, 2019
7085b9c
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into fe…
fedekunze Sep 10, 2019
0b6bbb5
Apply suggestions from code review
fedekunze Sep 11, 2019
e6bf7c4
address comments from review
fedekunze Sep 11, 2019
ac7aaef
Merge branch 'fedekunze/ADR-11-generalize-gen-accs' of https://github…
fedekunze Sep 11, 2019
e9d215a
remove genaccounts command
fedekunze Sep 11, 2019
5a69363
keep legacy genaccount migrations
fedekunze Sep 11, 2019
6c4b702
update genutil migration
fedekunze Sep 11, 2019
4e5e1a2
format
fedekunze Sep 11, 2019
6288793
migrate
fedekunze Sep 12, 2019
fe125bf
iterator
fedekunze Sep 12, 2019
1198733
Update godoc
alexanderbez Sep 12, 2019
f07fabc
Update godoc
alexanderbez Sep 12, 2019
2979564
Remove RegisterAccountTypeCodec call from supply
alexanderbez Sep 12, 2019
3636574
Merge branch 'master' into fedekunze/ADR-11-generalize-gen-accs
alexanderbez Sep 12, 2019
da91323
re-add RegisterAccountTypeCodec
alexanderbez Sep 12, 2019
42b04ee
Update godoc
alexanderbez Sep 12, 2019
7cef4d1
Add sim genesis concrete type
alexanderbez Sep 12, 2019
ae1569f
Fix migration
alexanderbez Sep 12, 2019
3d924db
Add ref for https://github.com/cosmos/cosmos-sdk/issues/5041
alexanderbez Sep 12, 2019
1d16d34
Add changelog entries
alexanderbez Sep 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### State Machine Breaking

* (genesis) [\#5017](https://github.com/cosmos/cosmos-sdk/pull/5017) The `x/genaccounts` module has been
deprecated and all components removed except the `legacy/` package. This requires changes to the
genesis state. Namely, `accounts` now exist under `app_state.auth.accounts`. The corresponding migration
logic has been implemented for v0.38 target version. Applications can migrate via:
`$ {appd} migrate v0.38 genesis.json`.

### API Breaking Changes

* (store) [\#4748](https://github.com/cosmos/cosmos-sdk/pull/4748) The `CommitMultiStore` interface
Expand All @@ -45,6 +53,8 @@ have this method perform a no-op.
* (modules) [\#4665](https://github.com/cosmos/cosmos-sdk/issues/4665) Refactored `x/gov` module structure and dev-UX:
* Prepare for module spec integration
* Update gov keys to use big endian encoding instead of little endian
* (modules) [\#5017](https://github.com/cosmos/cosmos-sdk/pull/5017) The `x/genaccounts` module has been
deprecated and all components removed except the `legacy/` package.

### Client Breaking Changes

Expand All @@ -68,6 +78,8 @@ and tx hash will be returned for specific Tendermint errors:

### Improvements

* (modules) [\#5017](https://github.com/cosmos/cosmos-sdk/pull/5017) The `x/auth` package now supports
generalized genesis accounts through the `GenesisAccount` interface.
* (modules) [\#4762](https://github.com/cosmos/cosmos-sdk/issues/4762) Deprecate remove and add permissions in ModuleAccount.
* (modules) [\#4760](https://github.com/cosmos/cosmos-sdk/issues/4760) update `x/auth` to match module spec.
* (modules) [\#4814](https://github.com/cosmos/cosmos-sdk/issues/4814) Add security contact to Validator description.
Expand Down
21 changes: 10 additions & 11 deletions docs/architecture/adr-011-generalize-genesis-accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,14 @@ func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) {
func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState {
params := ak.GetParams(ctx)

accounts := ak.GetAllAccounts(ctx)
// convert accounts to []GenesisAccounts type
genAccounts := make([]GenesisAccounts, len(accounts))
for i := range accounts {
ga := accounts[i].(GenesisAccount) // will panic if an account doesn't implement GenesisAccount
genAccounts[i] = ga
}

return NewGenesisState(params, accounts)
var genAccounts []exported.GenesisAccount
ak.IterateAccounts(ctx, func(account exported.Account) bool {
genAccount := account.(exported.GenesisAccount)
genAccounts = append(genAccounts, genAccount)
return false
})

return NewGenesisState(params, genAccounts)
}
```

Expand All @@ -64,11 +63,11 @@ The `auth` codec must have all custom account types registered to marshal them.
An example custom account definition:

```go
import authTypes "github.com/cosmos/cosmos-sdk/x/auth/types"
import authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

// Register the module account type with the auth module codec so it can decode module accounts stored in a genesis file
func init() {
authTypes.RegisterAccountTypeCodec(ModuleAccount{}, "cosmos-sdk/ModuleAccount")
authtypes.RegisterAccountTypeCodec(ModuleAccount{}, "cosmos-sdk/ModuleAccount")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally we should provided a function: SealAccountTypeCodec to be used after all auth types have been registered

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this function is necessary. The application will call {module}.ModuleCdc.Seal() somewhere near the end in the app constructor (e.g. gov.ModuleCdc.Seal()).

}

type ModuleAccount struct {
Expand Down
12 changes: 4 additions & 8 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
Expand All @@ -43,9 +42,9 @@ var (
// non-dependant module elements, such as codec registration
// and genesis verification.
ModuleBasics = module.NewBasicManager(
genaccounts.AppModuleBasic{},
genutil.AppModuleBasic{},
auth.AppModuleBasic{},
supply.AppModuleBasic{},
genutil.AppModuleBasic{},
bank.AppModuleBasic{},
staking.AppModuleBasic{},
mint.AppModuleBasic{},
Expand All @@ -55,7 +54,6 @@ var (
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
nft.AppModuleBasic{},
supply.AppModuleBasic{},
)

// module account permissions
Expand Down Expand Up @@ -178,7 +176,6 @@ func NewSimApp(
// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
app.mm = module.NewManager(
genaccounts.NewAppModule(app.AccountKeeper),
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.AccountKeeper),
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
Expand All @@ -202,8 +199,8 @@ func NewSimApp(
// NOTE: The genutils moodule must occur after staking so that pools are
// properly initialized with tokens from genesis accounts.
app.mm.SetOrderInitGenesis(
genaccounts.ModuleName, distr.ModuleName, staking.ModuleName,
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
auth.ModuleName, distr.ModuleName, staking.ModuleName,
bank.ModuleName, slashing.ModuleName, gov.ModuleName,
mint.ModuleName, supply.ModuleName, crisis.ModuleName, nft.ModuleName,
genutil.ModuleName,
)
Expand All @@ -216,7 +213,6 @@ func NewSimApp(
// NOTE: this is not required apps that don't use the simulator for fuzz testing
// transactions
app.sm = module.NewSimulationManager(
genaccounts.NewAppModule(app.AccountKeeper),
auth.NewAppModule(app.AccountKeeper),
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
Expand Down
52 changes: 52 additions & 0 deletions simapp/genesis_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package simapp

import (
"errors"

sdk "github.com/cosmos/cosmos-sdk/types"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/supply"
)

var _ authexported.GenesisAccount = (*SimGenesisAccount)(nil)

// SimGenesisAccount defines a type that implements the GenesisAccount interface
// to be used for simulation accounts in the genesis state.
type SimGenesisAccount struct {
*authtypes.BaseAccount

// vesting account fields
OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization
DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation
DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation
StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time)
EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time)

// module account fields
ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account
ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account
}

// Validate checks for errors on the vesting and module account parameters
func (sga SimGenesisAccount) Validate() error {
if !sga.OriginalVesting.IsZero() {
if sga.OriginalVesting.IsAnyGT(sga.Coins) {
return errors.New("vesting amount cannot be greater than total amount")
}
if sga.StartTime >= sga.EndTime {
return errors.New("vesting start-time cannot be before end-time")
}
}

if sga.ModuleName != "" {
ma := supply.ModuleAccount{
BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions,
}
if err := ma.Validate(); err != nil {
return err
}
}

return sga.BaseAccount.Validate()
}
98 changes: 98 additions & 0 deletions simapp/genesis_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package simapp_test

import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
)

func TestSimGenesisAccountValidate(t *testing.T) {
pubkey := secp256k1.GenPrivKey().PubKey()
addr := sdk.AccAddress(pubkey.Address())

vestingStart := time.Now().UTC()

coins := sdk.NewCoins(sdk.NewInt64Coin("test", 1000))
baseAcc := authtypes.NewBaseAccount(addr, nil, pubkey, 0, 0)
require.NoError(t, baseAcc.SetCoins(coins))

testCases := []struct {
name string
sga simapp.SimGenesisAccount
wantErr bool
}{
{
"valid basic account",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
},
false,
},
{
"invalid basic account with mismatching address/pubkey",
simapp.SimGenesisAccount{
BaseAccount: authtypes.NewBaseAccount(addr, nil, secp256k1.GenPrivKey().PubKey(), 0, 0),
},
true,
},
{
"valid basic account with module name",
simapp.SimGenesisAccount{
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(crypto.AddressHash([]byte("testmod"))), nil, nil, 0, 0),
ModuleName: "testmod",
},
false,
},
{
"valid basic account with invalid module name/pubkey pair",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
ModuleName: "testmod",
},
true,
},
{
"valid basic account with valid vesting attributes",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
OriginalVesting: coins,
StartTime: vestingStart.Unix(),
EndTime: vestingStart.Add(1 * time.Hour).Unix(),
},
false,
},
{
"valid basic account with invalid vesting end time",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
OriginalVesting: coins,
StartTime: vestingStart.Add(2 * time.Hour).Unix(),
EndTime: vestingStart.Add(1 * time.Hour).Unix(),
},
true,
},
{
"valid basic account with invalid original vesting coins",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
OriginalVesting: coins.Add(coins),
StartTime: vestingStart.Unix(),
EndTime: vestingStart.Add(1 * time.Hour).Unix(),
},
true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.wantErr, tc.sga.Validate() != nil)
})
}
}
11 changes: 7 additions & 4 deletions simapp/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/simulation"
)

Expand Down Expand Up @@ -126,9 +126,12 @@ func AppStateFromGenesisFileFn(r *rand.Rand, cdc *codec.Codec, genesisFile strin
var appState GenesisState
cdc.MustUnmarshalJSON(genesis.AppState, &appState)

accounts := genaccounts.GetGenesisStateFromAppState(cdc, appState)
var authGenesis auth.GenesisState
if appState[auth.ModuleName] != nil {
cdc.MustUnmarshalJSON(appState[auth.ModuleName], &authGenesis)
}

for _, acc := range accounts {
for _, acc := range authGenesis.Accounts {
// Pick a random private key, since we don't know the actual key
// This should be fine as it's only used for mock Tendermint validators
// and these keys are never actually used to sign by mock Tendermint.
Expand All @@ -140,7 +143,7 @@ func AppStateFromGenesisFileFn(r *rand.Rand, cdc *codec.Codec, genesisFile strin
privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed)

// create simulator accounts
simAcc := simulation.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: acc.Address}
simAcc := simulation.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: acc.GetAddress()}
newAccs = append(newAccs, simAcc)
}

Expand Down
10 changes: 7 additions & 3 deletions simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/supply"
)

Expand Down Expand Up @@ -45,13 +45,17 @@ func Setup(isCheckTx bool) *SimApp {

// SetupWithGenesisAccounts initializes a new SimApp with the passed in
// genesis accounts.
func SetupWithGenesisAccounts(genAccs genaccounts.GenesisAccounts) *SimApp {
func SetupWithGenesisAccounts(genAccs []authexported.GenesisAccount) *SimApp {
db := dbm.NewMemDB()
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)

// initialize the chain with the passed in genesis accounts
genesisState := NewDefaultGenesisState()
genesisState = genaccounts.SetGenesisStateInAppState(app.Codec(), genesisState, genaccounts.GenesisState(genAccs))

authGenesis := auth.NewGenesisState(auth.DefaultParams(), genAccs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the default params here btw?

genesisStateBz := app.cdc.MustMarshalJSON(authGenesis)
genesisState[auth.ModuleName] = genesisStateBz

stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
if err != nil {
panic(err)
Expand Down
2 changes: 2 additions & 0 deletions x/auth/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ var (
NewDelayedVestingAccount = types.NewDelayedVestingAccount
NewAccountRetriever = types.NewAccountRetriever
RegisterCodec = types.RegisterCodec
RegisterAccountTypeCodec = types.RegisterAccountTypeCodec
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
ValidateGenesis = types.ValidateGenesis
Sanitize = types.Sanitize
AddressStoreKey = types.AddressStoreKey
NewParams = types.NewParams
ParamKeyTable = types.ParamKeyTable
Expand Down
6 changes: 6 additions & 0 deletions x/auth/exported/exported.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ type VestingAccount interface {
GetDelegatedFree() sdk.Coins
GetDelegatedVesting() sdk.Coins
}

// GenesisAccount defines a genesis account that embeds an Account with validation capabilities.
type GenesisAccount interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! I'm a fan of this pattern

Account
Validate() error
}
17 changes: 16 additions & 1 deletion x/auth/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package auth

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
)

// InitGenesis - Init store state from genesis data
Expand All @@ -10,10 +11,24 @@ 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)

for _, a := range data.Accounts {
acc := ak.NewAccount(ctx, a)
ak.SetAccount(ctx, acc)
}
}

// ExportGenesis returns a GenesisState for a given context and keeper
func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState {
params := ak.GetParams(ctx)
return NewGenesisState(params)

var genAccounts []exported.GenesisAccount
ak.IterateAccounts(ctx, func(account exported.Account) bool {
genAccount := account.(exported.GenesisAccount)
genAccounts = append(genAccounts, genAccount)
return false
})

return NewGenesisState(params, genAccounts)
}
Loading