Merge PR #5017: ADR 011: Generalize Genesis Accounts Implementation

This commit is contained in:
Federico Kunze 2019-09-12 22:22:25 +03:00 committed by Alexander Bezobchuk
parent e660adc6e5
commit ed6366679c
39 changed files with 816 additions and 913 deletions

View File

@ -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
@ -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
@ -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.

View File

@ -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
}
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, accounts)
return NewGenesisState(params, genAccounts)
}
```
@ -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")
}
type ModuleAccount struct {

View File

@ -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"
@ -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{},
@ -55,7 +54,6 @@ var (
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
nft.AppModuleBasic{},
supply.AppModuleBasic{},
)
// module account permissions
@ -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),
@ -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,
)
@ -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),

52
simapp/genesis_account.go Normal file
View File

@ -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()
}

View File

@ -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)
})
}
}

View File

@ -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"
)
@ -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.
@ -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)
}

View File

@ -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"
)
@ -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)
genesisStateBz := app.cdc.MustMarshalJSON(authGenesis)
genesisState[auth.ModuleName] = genesisStateBz
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
if err != nil {
panic(err)

View File

@ -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

View File

@ -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 {
Account
Validate() error
}

View File

@ -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
@ -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)
}

View File

@ -0,0 +1,13 @@
package v038
import (
"encoding/json"
v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36"
)
// Migrate accepts exported genesis state from v0.34 and migrates it to v0.38
// genesis state.
func Migrate(oldGenState v036auth.GenesisState, accounts json.RawMessage) GenesisState {
return NewGenesisState(oldGenState.Params, accounts)
}

View File

@ -0,0 +1,124 @@
package v038
import (
"encoding/json"
"testing"
v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34"
v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36"
"github.com/stretchr/testify/require"
)
func TestMigrate(t *testing.T) {
var genesisState GenesisState
params := v034auth.Params{
MaxMemoCharacters: 10,
TxSigLimit: 10,
TxSizeCostPerByte: 10,
SigVerifyCostED25519: 10,
SigVerifyCostSecp256k1: 10,
}
rawAccounts := `[
{
"address": "cosmos1dfp05pasnts7a4lupn889vptjtrxzkk5f7027f",
"coins": [
{
"denom": "node0token",
"amount": "1000000000"
},
{
"denom": "stake",
"amount": "500000000"
}
],
"sequence_number": "0",
"account_number": "0",
"original_vesting": [],
"delegated_free": [],
"delegated_vesting": [],
"start_time": "0",
"end_time": "0",
"module_name": "",
"module_permissions": null
},
{
"address": "cosmos1f6dangl9ggdhuvkcwhswserr8fzra6vfzfjvh2",
"coins": [
{
"denom": "node1token",
"amount": "1000000000"
},
{
"denom": "stake",
"amount": "500000000"
}
],
"sequence_number": "0",
"account_number": "0",
"original_vesting": [],
"delegated_free": [],
"delegated_vesting": [],
"start_time": "0",
"end_time": "0",
"module_name": "",
"module_permissions": null
},
{
"address": "cosmos1gudmxhn5anh5m6m2rr4rsfhgvps8fchtgmk7a6",
"coins": [
{
"denom": "node2token",
"amount": "1000000000"
},
{
"denom": "stake",
"amount": "500000000"
}
],
"sequence_number": "0",
"account_number": "0",
"original_vesting": [],
"delegated_free": [],
"delegated_vesting": [],
"start_time": "0",
"end_time": "0",
"module_name": "",
"module_permissions": null
},
{
"address": "cosmos1kluvs8ff2s3hxad4jpmhvca4crqpcwn9xyhchv",
"coins": [
{
"denom": "node3token",
"amount": "1000000000"
},
{
"denom": "stake",
"amount": "500000000"
}
],
"sequence_number": "0",
"account_number": "0",
"original_vesting": [],
"delegated_free": [],
"delegated_vesting": [],
"start_time": "0",
"end_time": "0",
"module_name": "",
"module_permissions": null
}
]`
require.NotPanics(t, func() {
genesisState = Migrate(
v036auth.GenesisState{
Params: params,
},
json.RawMessage(rawAccounts),
)
})
require.Equal(t, genesisState, GenesisState{Params: params, Accounts: json.RawMessage(rawAccounts)})
}

View File

@ -0,0 +1,26 @@
package v038
import (
"encoding/json"
v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34"
)
// DONTCOVER
// nolint
const (
ModuleName = "auth"
)
type GenesisState struct {
Params v034auth.Params `json:"params"`
Accounts json.RawMessage `json:"accounts"`
}
func NewGenesisState(params v034auth.Params, accounts json.RawMessage) GenesisState {
return GenesisState{
Params: params,
Accounts: accounts,
}
}

View File

@ -7,7 +7,9 @@ import (
"math/rand"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
@ -78,11 +80,50 @@ func RandomizedGenState(simState *module.SimulationState) {
func(r *rand.Rand) { sigVerifyCostED25519 = GenSigVerifyCostSECP256K1(r) },
)
authGenesis := types.NewGenesisState(
types.NewParams(maxMemoChars, txSigLimit, txSizeCostPerByte,
sigVerifyCostED25519, sigVerifyCostSECP256K1),
)
params := types.NewParams(maxMemoChars, txSigLimit, txSizeCostPerByte,
sigVerifyCostED25519, sigVerifyCostSECP256K1)
genesisAccs := RandomGenesisAccounts(simState)
authGenesis := types.NewGenesisState(params, genesisAccs)
fmt.Printf("Selected randomly generated auth parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, authGenesis.Params))
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
}
// RandomGenesisAccounts returns randomly generated genesis accounts
func RandomGenesisAccounts(simState *module.SimulationState) (genesisAccs []exported.GenesisAccount) {
for i, acc := range simState.Accounts {
coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))}
bacc := types.NewBaseAccountWithAddress(acc.Address)
if err := bacc.SetCoins(coins); err != nil {
panic(err)
}
var gacc exported.GenesisAccount
gacc = &bacc
// Only consider making a vesting account once the initial bonded validator
// set is exhausted due to needing to track DelegatedVesting.
if int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50 {
var endTime int64
startTime := simState.GenTimestamp.Unix()
// Allow for some vesting accounts to vest very quickly while others very slowly.
if simState.Rand.Intn(100) < 50 {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*24*30))))
} else {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*12))))
}
if simState.Rand.Intn(100) < 50 {
gacc = types.NewContinuousVestingAccount(&bacc, startTime, endTime)
} else {
gacc = types.NewDelayedVestingAccount(&bacc, endTime)
}
}
genesisAccs = append(genesisAccs, gacc)
}
return
}

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"errors"
"fmt"
"time"
@ -16,6 +17,7 @@ import (
// BaseAccount
var _ exported.Account = (*BaseAccount)(nil)
var _ exported.GenesisAccount = (*BaseAccount)(nil)
// BaseAccount - a base account structure.
// This can be extended by embedding within in your AppAccount.
@ -169,6 +171,16 @@ func (acc BaseAccount) MarshalYAML() (interface{}, error) {
return string(bs), err
}
// Validate checks for errors on the account fields
func (acc BaseAccount) Validate() error {
if acc.PubKey != nil && acc.Address != nil &&
!bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) {
return errors.New("pubkey and address pair is invalid")
}
return nil
}
//-----------------------------------------------------------------------------
// Base Vesting Account
@ -346,10 +358,20 @@ func (bva BaseVestingAccount) GetDelegatedVesting() sdk.Coins {
return bva.DelegatedVesting
}
// Validate checks for errors on the account fields
func (bva BaseVestingAccount) Validate() error {
if (bva.Coins.IsZero() && !bva.OriginalVesting.IsZero()) ||
bva.OriginalVesting.IsAnyGT(bva.Coins) {
return errors.New("vesting amount cannot be greater than total amount")
}
return bva.BaseAccount.Validate()
}
//-----------------------------------------------------------------------------
// Continuous Vesting Account
var _ exported.VestingAccount = (*ContinuousVestingAccount)(nil)
var _ exported.GenesisAccount = (*ContinuousVestingAccount)(nil)
// ContinuousVestingAccount implements the VestingAccount interface. It
// continuously vests by unlocking coins linearly with respect to time.
@ -467,10 +489,20 @@ func (cva *ContinuousVestingAccount) GetEndTime() int64 {
return cva.EndTime
}
// Validate checks for errors on the account fields
func (cva ContinuousVestingAccount) Validate() error {
if cva.GetStartTime() >= cva.GetEndTime() {
return errors.New("vesting start-time cannot be before end-time")
}
return cva.BaseVestingAccount.Validate()
}
//-----------------------------------------------------------------------------
// Delayed Vesting Account
var _ exported.VestingAccount = (*DelayedVestingAccount)(nil)
var _ exported.GenesisAccount = (*DelayedVestingAccount)(nil)
// DelayedVestingAccount implements the VestingAccount interface. It vests all
// coins after a specific time, but non prior. In other words, it keeps them
@ -535,3 +567,8 @@ func (dva *DelayedVestingAccount) GetStartTime() int64 {
func (dva *DelayedVestingAccount) GetEndTime() int64 {
return dva.EndTime
}
// Validate checks for errors on the account fields
func (dva DelayedVestingAccount) Validate() error {
return dva.BaseVestingAccount.Validate()
}

View File

@ -1,14 +1,17 @@
package types
import (
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
)
var (
@ -479,3 +482,73 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) {
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, dva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 75)}, dva.GetCoins())
}
func TestGenesisAccountValidate(t *testing.T) {
pubkey := secp256k1.GenPrivKey().PubKey()
addr := sdk.AccAddress(pubkey.Address())
baseAcc := NewBaseAccount(addr, nil, pubkey, 0, 0)
tests := []struct {
name string
acc exported.GenesisAccount
expErr error
}{
{
"valid base account",
baseAcc,
nil,
},
{
"invalid base valid account",
NewBaseAccount(addr, sdk.NewCoins(), secp256k1.GenPrivKey().PubKey(), 0, 0),
errors.New("pubkey and address pair is invalid"),
},
{
"valid base vesting account",
NewBaseVestingAccount(baseAcc, sdk.NewCoins(), nil, nil, 100),
nil,
},
{
"invalid vesting amount; empty Coins",
NewBaseVestingAccount(
NewBaseAccount(addr, sdk.NewCoins(), pubkey, 0, 0),
sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)},
nil, nil, 100,
),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"invalid vesting amount; OriginalVesting > Coins",
NewBaseVestingAccount(
NewBaseAccount(addr, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)), pubkey, 0, 0),
sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)},
nil, nil, 100,
),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"invalid vesting amount with multi coins",
NewBaseVestingAccount(
NewBaseAccount(addr, sdk.NewCoins(sdk.NewInt64Coin("uatom", 50), sdk.NewInt64Coin("eth", 50)), pubkey, 0, 0),
sdk.NewCoins(sdk.NewInt64Coin("uatom", 100), sdk.NewInt64Coin("eth", 20)),
nil, nil, 100,
),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"valid continuous vesting account",
NewContinuousVestingAccount(baseAcc, 100, 200),
nil,
},
{
"invalid vesting times",
NewContinuousVestingAccount(baseAcc, 1654668078, 1554668078),
errors.New("vesting start-time cannot be before end-time"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.acc.Validate()
require.Equal(t, tt.expErr, err)
})
}
}

View File

@ -5,8 +5,12 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/exported"
)
// ModuleCdc auth module wide codec
var ModuleCdc = codec.New()
// RegisterCodec registers concrete types on the codec
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterInterface((*exported.GenesisAccount)(nil), nil)
cdc.RegisterInterface((*exported.Account)(nil), nil)
cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil)
cdc.RegisterInterface((*exported.VestingAccount)(nil), nil)
@ -16,12 +20,13 @@ func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(StdTx{}, "cosmos-sdk/StdTx", nil)
}
// module wide codec
var ModuleCdc *codec.Codec
// RegisterAccountTypeCodec registers an external account type defined in
// another module for the internal ModuleCdc.
func RegisterAccountTypeCodec(o interface{}, name string) {
ModuleCdc.RegisterConcrete(o, name, nil)
}
func init() {
ModuleCdc = codec.New()
RegisterCodec(ModuleCdc)
codec.RegisterCrypto(ModuleCdc)
ModuleCdc.Seal()
}

View File

@ -2,40 +2,71 @@ package types
import (
"fmt"
"sort"
"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"`
Params Params `json:"params" yaml:"params"`
Accounts []exported.GenesisAccount `json:"accounts" yaml:"accounts"`
}
// NewGenesisState - Create a new genesis state
func NewGenesisState(params Params) GenesisState {
return GenesisState{params}
func NewGenesisState(params Params, accounts []exported.GenesisAccount) GenesisState {
return GenesisState{
Params: params,
Accounts: accounts,
}
}
// DefaultGenesisState - Return a default genesis state
func DefaultGenesisState() GenesisState {
return NewGenesisState(DefaultParams())
return NewGenesisState(DefaultParams(), []exported.GenesisAccount{})
}
// ValidateGenesis performs basic validation of auth genesis data returning an
// error for any failed validation criteria.
func ValidateGenesis(data GenesisState) error {
if data.Params.TxSigLimit == 0 {
return fmt.Errorf("invalid tx signature limit: %d", data.Params.TxSigLimit)
if err := data.Params.Validate(); err != nil {
return err
}
if data.Params.SigVerifyCostED25519 == 0 {
return fmt.Errorf("invalid ED25519 signature verification cost: %d", data.Params.SigVerifyCostED25519)
return validateGenAccounts(data.Accounts)
}
// Sanitize sorts accounts and coin sets.
func Sanitize(genAccs []exported.GenesisAccount) []exported.GenesisAccount {
sort.Slice(genAccs, func(i, j int) bool {
return genAccs[i].GetAccountNumber() < genAccs[j].GetAccountNumber()
})
for _, acc := range genAccs {
if err := acc.SetCoins(acc.GetCoins().Sort()); err != nil {
panic(err)
}
}
if data.Params.SigVerifyCostSecp256k1 == 0 {
return fmt.Errorf("invalid SECK256k1 signature verification cost: %d", data.Params.SigVerifyCostSecp256k1)
}
if data.Params.MaxMemoCharacters == 0 {
return fmt.Errorf("invalid max memo characters: %d", data.Params.MaxMemoCharacters)
}
if data.Params.TxSizeCostPerByte == 0 {
return fmt.Errorf("invalid tx size cost per byte: %d", data.Params.TxSizeCostPerByte)
return genAccs
}
func validateGenAccounts(accounts []exported.GenesisAccount) error {
addrMap := make(map[string]bool, len(accounts))
for _, acc := range accounts {
// check for duplicated accounts
addrStr := acc.GetAddress().String()
if _, ok := addrMap[addrStr]; ok {
return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr)
}
addrMap[addrStr] = true
// check account specific validation
if err := acc.Validate(); err != nil {
return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error())
}
}
return nil
}

View File

@ -0,0 +1,82 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
)
func TestSanitize(t *testing.T) {
addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
authAcc1 := NewBaseAccountWithAddress(addr1)
authAcc1.SetCoins(sdk.Coins{
sdk.NewInt64Coin("bcoin", 150),
sdk.NewInt64Coin("acoin", 150),
})
authAcc1.SetAccountNumber(1)
addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
authAcc2 := NewBaseAccountWithAddress(addr2)
authAcc2.SetCoins(sdk.Coins{
sdk.NewInt64Coin("acoin", 150),
sdk.NewInt64Coin("bcoin", 150),
})
genAccs := []exported.GenesisAccount{&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)
require.False(t, genAccs[0].GetAccountNumber() > genAccs[1].GetAccountNumber())
require.Equal(t, genAccs[1].GetAddress(), addr1)
require.Equal(t, genAccs[1].GetCoins()[0].Denom, "acoin")
require.Equal(t, genAccs[1].GetCoins()[1].Denom, "bcoin")
}
var (
pk1 = ed25519.GenPrivKey().PubKey()
pk2 = ed25519.GenPrivKey().PubKey()
addr1 = sdk.ValAddress(pk1.Address())
addr2 = sdk.ValAddress(pk2.Address())
)
// require duplicate accounts fails validation
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[0] = &acc1
genAccs[1] = &acc1
require.Error(t, validateGenAccounts(genAccs))
}
// require invalid vesting account fails validation (invalid end time)
func TestValidateGenesisInvalidAccounts(t *testing.T) {
acc1 := NewBaseAccountWithAddress(sdk.AccAddress(addr1))
acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150))
baseVestingAcc := NewBaseVestingAccount(&acc1, acc1.Coins.Add(acc1.Coins), nil, nil, 1548775410)
acc2 := NewBaseAccountWithAddress(sdk.AccAddress(addr2))
acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150))
genAccs := make([]exported.GenesisAccount, 2)
genAccs[0] = baseVestingAcc
genAccs[1] = &acc2
require.Error(t, validateGenAccounts(genAccs))
baseVestingAcc.OriginalVesting = acc1.Coins
genAccs[0] = baseVestingAcc
require.NoError(t, validateGenAccounts(genAccs))
genAccs[0] = NewContinuousVestingAccountRaw(baseVestingAcc, 1548888000)
require.Error(t, validateGenAccounts(genAccs))
}

View File

@ -100,3 +100,23 @@ func (p Params) String() string {
sb.WriteString(fmt.Sprintf("SigVerifyCostSecp256k1: %d\n", p.SigVerifyCostSecp256k1))
return sb.String()
}
// Validate checks that the parameters have valid values.
func (p Params) Validate() error {
if p.TxSigLimit == 0 {
return fmt.Errorf("invalid tx signature limit: %d", p.TxSigLimit)
}
if p.SigVerifyCostED25519 == 0 {
return fmt.Errorf("invalid ED25519 signature verification cost: %d", p.SigVerifyCostED25519)
}
if p.SigVerifyCostSecp256k1 == 0 {
return fmt.Errorf("invalid SECK256k1 signature verification cost: %d", p.SigVerifyCostSecp256k1)
}
if p.MaxMemoCharacters == 0 {
return fmt.Errorf("invalid max memo characters: %d", p.MaxMemoCharacters)
}
if p.TxSizeCostPerByte == 0 {
return fmt.Errorf("invalid tx size cost per byte: %d", p.TxSizeCostPerByte)
}
return nil
}

View File

@ -12,8 +12,8 @@ import (
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
)
type (
@ -93,7 +93,7 @@ func TestSendNotEnoughBalance(t *testing.T) {
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 67)},
}
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc)}
genAccs := []authexported.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
@ -125,7 +125,7 @@ func TestSendToModuleAcc(t *testing.T) {
Coins: coins,
}
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc)}
genAccs := []authexported.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
@ -156,7 +156,7 @@ func TestMsgMultiSendWithAccounts(t *testing.T) {
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 67)},
}
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc)}
genAccs := []authexported.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
@ -217,7 +217,7 @@ func TestMsgMultiSendMultipleOut(t *testing.T) {
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)},
}
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc1), genaccounts.NewGenesisAccount(acc2)}
genAccs := []authexported.GenesisAccount{acc1, acc2}
app := simapp.SetupWithGenesisAccounts(genAccs)
testCases := []appTestCase{
@ -261,7 +261,7 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) {
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)},
}
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc1), genaccounts.NewGenesisAccount(acc2), genaccounts.NewGenesisAccount(acc4)}
genAccs := []authexported.GenesisAccount{acc1, acc2, acc4}
app := simapp.SetupWithGenesisAccounts(genAccs)
testCases := []appTestCase{
@ -299,7 +299,7 @@ func TestMsgMultiSendDependent(t *testing.T) {
err = acc2.SetAccountNumber(1)
require.NoError(t, err)
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(&acc1), genaccounts.NewGenesisAccount(&acc2)}
genAccs := []authexported.GenesisAccount{&acc1, &acc2}
app := simapp.SetupWithGenesisAccounts(genAccs)
testCases := []appTestCase{

View File

@ -8,7 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/simapp"
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/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
@ -17,14 +17,14 @@ var moduleAccAddr = supply.NewModuleAddress(staking.BondedPoolName)
func BenchmarkOneBankSendTxPerBlock(b *testing.B) {
// Add an account at genesis
acc := &auth.BaseAccount{
acc := auth.BaseAccount{
Address: addr1,
// Some value conceivably higher than the benchmarks would ever go
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 100000000000)},
}
// Construct genesis state
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc)}
genAccs := []authexported.GenesisAccount{&acc}
benchmarkApp := simapp.SetupWithGenesisAccounts(genAccs)
// Precompute all txs
@ -46,14 +46,14 @@ func BenchmarkOneBankSendTxPerBlock(b *testing.B) {
func BenchmarkOneBankMultiSendTxPerBlock(b *testing.B) {
// Add an account at genesis
acc := &auth.BaseAccount{
acc := auth.BaseAccount{
Address: addr1,
// Some value conceivably higher than the benchmarks would ever go
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 100000000000)},
}
// Construct genesis state
genAccs := []genaccounts.GenesisAccount{genaccounts.NewGenesisAccount(acc)}
genAccs := []authexported.GenesisAccount{&acc}
benchmarkApp := simapp.SetupWithGenesisAccounts(genAccs)
// Precompute all txs

View File

@ -1,32 +0,0 @@
// nolint
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types
package genaccounts
import (
"github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types"
)
const (
ModuleName = types.ModuleName
)
var (
// functions aliases
NewGenesisAccountRaw = types.NewGenesisAccountRaw
NewGenesisAccount = types.NewGenesisAccount
NewGenesisAccountI = types.NewGenesisAccountI
GetGenesisStateFromAppState = types.GetGenesisStateFromAppState
SetGenesisStateInAppState = types.SetGenesisStateInAppState
ValidateGenesis = types.ValidateGenesis
// variable aliases
ModuleCdc = types.ModuleCdc
)
type (
GenesisAccount = types.GenesisAccount
GenesisAccounts = types.GenesisAccounts
GenesisState = types.GenesisState
)

View File

@ -1,107 +0,0 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
)
const (
flagClientHome = "home-client"
flagVestingStart = "vesting-start-time"
flagVestingEnd = "vesting-end-time"
flagVestingAmt = "vesting-amount"
)
// AddGenesisAccountCmd returns add-genesis-account cobra Command.
func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec,
defaultNodeHome, defaultClientHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
Short: "Add genesis account to genesis.json",
Args: cobra.ExactArgs(2),
RunE: func(_ *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
if err != nil {
return err
}
info, err := kb.Get(args[0])
if err != nil {
return err
}
addr = info.GetAddress()
}
coins, err := sdk.ParseCoins(args[1])
if err != nil {
return err
}
vestingStart := viper.GetInt64(flagVestingStart)
vestingEnd := viper.GetInt64(flagVestingEnd)
vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt))
if err != nil {
return err
}
genAcc := genaccounts.NewGenesisAccountRaw(addr, coins, vestingAmt, vestingStart, vestingEnd, "", "")
if err := genAcc.Validate(); err != nil {
return err
}
// retrieve the app state
genFile := config.GenesisFile()
appState, genDoc, err := genutil.GenesisStateFromGenFile(cdc, genFile)
if err != nil {
return err
}
// add genesis account to the app state
var genesisAccounts genaccounts.GenesisAccounts
cdc.MustUnmarshalJSON(appState[genaccounts.ModuleName], &genesisAccounts)
if genesisAccounts.Contains(addr) {
return fmt.Errorf("cannot add account at existing address %v", addr)
}
genesisAccounts = append(genesisAccounts, genAcc)
genesisStateBz := cdc.MustMarshalJSON(genaccounts.GenesisState(genesisAccounts))
appState[genaccounts.ModuleName] = genesisStateBz
appStateJSON, err := cdc.MarshalJSON(appState)
if err != nil {
return err
}
// export app state
genDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(genDoc, genFile)
},
}
cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, defaultClientHome, "client's home directory")
cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
return cmd
}

View File

@ -1,9 +1,15 @@
/*
Package genaccounts contains specialized functionality for initializing
accounts from genesis including:
- genesis account validation,
- initchain processing of genesis accounts,
- export processing (to genesis) of accounts,
- server command for adding accounts to the genesis file.
Package genaccounts is now deprecated.
IMPORTANT: This module has been replaced by ADR 011: Generalize Module Accounts.
The ADR can be found here: https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-011-generalize-genesis-accounts.md.
Genesis accounts that existed in the genesis application state under `app_state.accounts`
now exists under the x/auth module's genesis state under the `app_state.auth.accounts` key.
Migration can be performed via x/auth/legacy/v0_38/migrate.go. In addition, because genesis
accounts are now generalized via an interface, it is now up to the application to
define the concrete types and the respective client logic to add them to a genesis
state/file. For an example implementation of the `add-genesis-account` command please
refer to https://github.com/cosmos/gaia/pull/122.
*/
package genaccounts

View File

@ -1,39 +0,0 @@
package genaccounts
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types"
)
// InitGenesis initializes accounts and deliver genesis transactions
func InitGenesis(ctx sdk.Context, _ *codec.Codec, accountKeeper types.AccountKeeper, genesisState GenesisState) {
genesisState.Sanitize()
// load the accounts
for _, gacc := range genesisState {
acc := gacc.ToAccount()
acc = accountKeeper.NewAccount(ctx, acc) // set account number
accountKeeper.SetAccount(ctx, acc)
}
}
// ExportGenesis exports genesis for all accounts
func ExportGenesis(ctx sdk.Context, accountKeeper types.AccountKeeper) GenesisState {
// iterate to get the accounts
accounts := []GenesisAccount{}
accountKeeper.IterateAccounts(ctx,
func(acc authexported.Account) (stop bool) {
account, err := NewGenesisAccountI(acc)
if err != nil {
panic(err)
}
accounts = append(accounts, account)
return false
},
)
return accounts
}

View File

@ -1,17 +0,0 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
)
// ModuleName is "accounts"
const ModuleName = "accounts"
// ModuleCdc - generic sealed codec to be used throughout this module
var ModuleCdc *codec.Codec
func init() {
ModuleCdc = codec.New()
codec.RegisterCrypto(ModuleCdc)
ModuleCdc.Seal()
}

View File

@ -1,13 +0,0 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
)
// AccountKeeper defines the expected account keeper (noalias)
type AccountKeeper interface {
NewAccount(sdk.Context, authexported.Account) authexported.Account
SetAccount(sdk.Context, authexported.Account)
IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool))
}

View File

@ -1,151 +0,0 @@
package types
import (
"errors"
"fmt"
"strings"
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"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// GenesisAccount is a struct for account initialization used exclusively during genesis
type GenesisAccount struct {
Address sdk.AccAddress `json:"address" yaml:"address"`
Coins sdk.Coins `json:"coins" yaml:"coins"`
Sequence uint64 `json:"sequence_number" yaml:"sequence_number"`
AccountNumber uint64 `json:"account_number" yaml:"account_number"`
// 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 (ga GenesisAccount) Validate() error {
if !ga.OriginalVesting.IsZero() {
if ga.OriginalVesting.IsAnyGT(ga.Coins) {
return errors.New("vesting amount cannot be greater than total amount")
}
if ga.StartTime >= ga.EndTime {
return errors.New("vesting start-time cannot be before end-time")
}
}
// don't allow blank (i.e just whitespaces) on the module name
if ga.ModuleName != "" && strings.TrimSpace(ga.ModuleName) == "" {
return errors.New("module account name cannot be blank")
}
return nil
}
// NewGenesisAccountRaw creates a new GenesisAccount object
func NewGenesisAccountRaw(address sdk.AccAddress, coins,
vestingAmount sdk.Coins, vestingStartTime, vestingEndTime int64,
module string, permissions ...string) GenesisAccount {
return GenesisAccount{
Address: address,
Coins: coins,
Sequence: 0,
AccountNumber: 0, // ignored set by the account keeper during InitGenesis
OriginalVesting: vestingAmount,
DelegatedFree: sdk.Coins{}, // ignored
DelegatedVesting: sdk.Coins{}, // ignored
StartTime: vestingStartTime,
EndTime: vestingEndTime,
ModuleName: module,
ModulePermissions: permissions,
}
}
// NewGenesisAccount creates a GenesisAccount instance from a BaseAccount.
func NewGenesisAccount(acc *authtypes.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
Coins: acc.Coins,
AccountNumber: acc.AccountNumber,
Sequence: acc.Sequence,
}
}
// NewGenesisAccountI creates a GenesisAccount instance from an Account interface.
func NewGenesisAccountI(acc authexported.Account) (GenesisAccount, error) {
gacc := GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
}
if err := gacc.Validate(); err != nil {
return gacc, err
}
switch acc := acc.(type) {
case authexported.VestingAccount:
gacc.OriginalVesting = acc.GetOriginalVesting()
gacc.DelegatedFree = acc.GetDelegatedFree()
gacc.DelegatedVesting = acc.GetDelegatedVesting()
gacc.StartTime = acc.GetStartTime()
gacc.EndTime = acc.GetEndTime()
case supplyexported.ModuleAccountI:
gacc.ModuleName = acc.GetName()
gacc.ModulePermissions = acc.GetPermissions()
}
return gacc, nil
}
// ToAccount converts a GenesisAccount to an Account interface
func (ga *GenesisAccount) ToAccount() authexported.Account {
bacc := authtypes.NewBaseAccount(ga.Address, ga.Coins.Sort(), nil, ga.AccountNumber, ga.Sequence)
// vesting accounts
if !ga.OriginalVesting.IsZero() {
baseVestingAcc := authtypes.NewBaseVestingAccount(
bacc, ga.OriginalVesting, ga.DelegatedFree,
ga.DelegatedVesting, ga.EndTime,
)
switch {
case ga.StartTime != 0 && ga.EndTime != 0:
return authtypes.NewContinuousVestingAccountRaw(baseVestingAcc, ga.StartTime)
case ga.EndTime != 0:
return authtypes.NewDelayedVestingAccountRaw(baseVestingAcc)
default:
panic(fmt.Sprintf("invalid genesis vesting account: %+v", ga))
}
}
// module accounts
if ga.ModuleName != "" {
return supply.NewModuleAccount(bacc, ga.ModuleName, ga.ModulePermissions...)
}
return bacc
}
// GenesisAccounts defines a set of genesis account
type GenesisAccounts []GenesisAccount
// Contains checks if a set of genesis accounts contain an address
func (gaccs GenesisAccounts) Contains(acc sdk.AccAddress) bool {
for _, gacc := range gaccs {
if gacc.Address.Equals(acc) {
return true
}
}
return false
}

View File

@ -1,97 +0,0 @@
package types
import (
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/supply"
)
func TestGenesisAccountValidate(t *testing.T) {
addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
tests := []struct {
name string
acc GenesisAccount
expErr error
}{
{
"valid account",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, "", ""),
nil,
},
{
"valid module account",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, "mint", supply.Minter),
nil,
},
{
"invalid vesting amount",
NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
sdk.NewCoins(sdk.NewInt64Coin("stake", 100)), 0, 0, "", ""),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"invalid vesting amount with multi coins",
NewGenesisAccountRaw(addr,
sdk.NewCoins(sdk.NewInt64Coin("uatom", 50), sdk.NewInt64Coin("eth", 50)),
sdk.NewCoins(sdk.NewInt64Coin("uatom", 100), sdk.NewInt64Coin("eth", 20)),
0, 0, "", ""),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"invalid vesting times",
NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), 1654668078, 1554668078, "", ""),
errors.New("vesting start-time cannot be before end-time"),
},
{
"invalid module account name",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, " ", ""),
errors.New("module account name cannot be blank"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.acc.Validate()
require.Equal(t, tt.expErr, err)
})
}
}
func TestToAccount(t *testing.T) {
priv := ed25519.GenPrivKey()
addr := sdk.AccAddress(priv.PubKey().Address())
// base account
authAcc := authtypes.NewBaseAccountWithAddress(addr)
authAcc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)))
genAcc := NewGenesisAccount(&authAcc)
acc := genAcc.ToAccount()
require.IsType(t, &authtypes.BaseAccount{}, acc)
require.Equal(t, &authAcc, acc.(*authtypes.BaseAccount))
// vesting account
vacc := authtypes.NewContinuousVestingAccount(
&authAcc, time.Now().Unix(), time.Now().Add(24*time.Hour).Unix(),
)
genAcc, err := NewGenesisAccountI(vacc)
require.NoError(t, err)
acc = genAcc.ToAccount()
require.IsType(t, &authtypes.ContinuousVestingAccount{}, acc)
require.Equal(t, vacc, acc.(*authtypes.ContinuousVestingAccount))
// module account
macc := supply.NewEmptyModuleAccount("mint", supply.Minter)
genAcc, err = NewGenesisAccountI(macc)
require.NoError(t, err)
acc = genAcc.ToAccount()
require.IsType(t, &supply.ModuleAccount{}, acc)
require.Equal(t, macc, acc.(*supply.ModuleAccount))
}

View File

@ -1,77 +0,0 @@
package types
import (
"encoding/json"
"fmt"
"sort"
"time"
"github.com/cosmos/cosmos-sdk/codec"
)
// GenesisState is a wrapper for GenAccounts
type GenesisState GenesisAccounts
// GetGenesisStateFromAppState gets the genesis state from the expected app 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
}
// SetGenesisStateInAppState sets the genesis state within the expected app state
func SetGenesisStateInAppState(cdc *codec.Codec,
appState map[string]json.RawMessage, genesisState GenesisState) map[string]json.RawMessage {
genesisStateBz := cdc.MustMarshalJSON(genesisState)
appState[ModuleName] = genesisStateBz
return appState
}
// Sanitize sorts accounts and coin sets.
func (gs GenesisState) Sanitize() {
sort.Slice(gs, func(i, j int) bool {
return gs[i].AccountNumber < gs[j].AccountNumber
})
for _, acc := range gs {
acc.Coins = acc.Coins.Sort()
}
}
// ValidateGenesis performs validation of genesis accounts. It
// ensures that there are no duplicate accounts in the genesis state and any
// provided vesting accounts are valid.
func ValidateGenesis(genesisState GenesisState) error {
addrMap := make(map[string]bool, len(genesisState))
for _, acc := range genesisState {
addrStr := acc.Address.String()
// disallow any duplicate accounts
if _, ok := addrMap[addrStr]; ok {
return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr)
}
// validate any vesting fields
if !acc.OriginalVesting.IsZero() {
if acc.EndTime == 0 {
return fmt.Errorf("missing end time for vesting account; address: %s", addrStr)
}
if acc.StartTime >= acc.EndTime {
return fmt.Errorf(
"vesting start time must before end time; address: %s, start: %s, end: %s",
addrStr,
time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339),
time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339),
)
}
}
addrMap[addrStr] = true
}
return nil
}

View File

@ -1,86 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
func TestSanitize(t *testing.T) {
addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
authAcc1 := authtypes.NewBaseAccountWithAddress(addr1)
authAcc1.SetCoins(sdk.Coins{
sdk.NewInt64Coin("bcoin", 150),
sdk.NewInt64Coin("acoin", 150),
})
authAcc1.SetAccountNumber(1)
genAcc1 := NewGenesisAccount(&authAcc1)
addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
authAcc2 := authtypes.NewBaseAccountWithAddress(addr2)
authAcc2.SetCoins(sdk.Coins{
sdk.NewInt64Coin("acoin", 150),
sdk.NewInt64Coin("bcoin", 150),
})
genAcc2 := NewGenesisAccount(&authAcc2)
genesisState := GenesisState([]GenesisAccount{genAcc1, genAcc2})
require.NoError(t, ValidateGenesis(genesisState))
require.True(t, genesisState[0].AccountNumber > genesisState[1].AccountNumber)
require.Equal(t, genesisState[0].Coins[0].Denom, "bcoin")
require.Equal(t, genesisState[0].Coins[1].Denom, "acoin")
require.Equal(t, genesisState[1].Address, addr2)
genesisState.Sanitize()
require.False(t, genesisState[0].AccountNumber > genesisState[1].AccountNumber)
require.Equal(t, genesisState[1].Address, addr1)
require.Equal(t, genesisState[1].Coins[0].Denom, "acoin")
require.Equal(t, genesisState[1].Coins[1].Denom, "bcoin")
}
var (
pk1 = ed25519.GenPrivKey().PubKey()
pk2 = ed25519.GenPrivKey().PubKey()
addr1 = sdk.ValAddress(pk1.Address())
addr2 = sdk.ValAddress(pk2.Address())
)
// require duplicate accounts fails validation
func TestValidateGenesisDuplicateAccounts(t *testing.T) {
acc1 := authtypes.NewBaseAccountWithAddress(sdk.AccAddress(addr1))
acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150))
genAccs := make([]GenesisAccount, 2)
genAccs[0] = NewGenesisAccount(&acc1)
genAccs[1] = NewGenesisAccount(&acc1)
genesisState := GenesisState(genAccs)
err := ValidateGenesis(genesisState)
require.Error(t, err)
}
// require invalid vesting account fails validation (invalid end time)
func TestValidateGenesisInvalidAccounts(t *testing.T) {
acc1 := authtypes.NewBaseAccountWithAddress(sdk.AccAddress(addr1))
acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150))
acc2 := authtypes.NewBaseAccountWithAddress(sdk.AccAddress(addr2))
acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150))
genAccs := make([]GenesisAccount, 2)
genAccs[0] = NewGenesisAccount(&acc1)
genAccs[1] = NewGenesisAccount(&acc2)
genesisState := GenesisState(genAccs)
genesisState[0].OriginalVesting = genesisState[0].Coins
err := ValidateGenesis(genesisState)
require.Error(t, err)
genesisState[0].StartTime = 1548888000
genesisState[0].EndTime = 1548775410
err = ValidateGenesis(genesisState)
require.Error(t, err)
}

View File

@ -1,155 +0,0 @@
package genaccounts
import (
"encoding/json"
"math/rand"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types"
"github.com/cosmos/cosmos-sdk/x/genaccounts/simulation"
sim "github.com/cosmos/cosmos-sdk/x/simulation"
)
var (
_ module.AppModuleGenesis = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleSimulation = AppModuleSimulation{}
)
// AppModuleBasic defines the basic application module used by the genesis accounts module.
type AppModuleBasic struct{}
// Name returns the genesis accounts module's name.
func (AppModuleBasic) Name() string {
return ModuleName
}
// RegisterCodec performs a no-op.
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {}
// DefaultGenesis returns default genesis state as raw bytes for the genesis accounts
// module.
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return ModuleCdc.MustMarshalJSON(GenesisState{})
}
// ValidateGenesis performs genesis state validation for the genesis accounts module.
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
var data GenesisState
err := ModuleCdc.UnmarshalJSON(bz, &data)
if err != nil {
return err
}
return ValidateGenesis(data)
}
// RegisterRESTRoutes registers no REST routes for the genesis accounts module.
func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {}
// GetTxCmd returns no root tx command for the genesis accounts module.
func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil }
// GetQueryCmd returns no root query command for the genesis accounts module.
func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil }
// extra function from sdk.AppModuleBasic
// IterateGenesisAccounts iterates over the genesis accounts and perform an operation at each of them
// - to used by other modules
func (AppModuleBasic) IterateGenesisAccounts(cdc *codec.Codec, appGenesis map[string]json.RawMessage,
iterateFn func(exported.Account) (stop bool)) {
genesisState := GetGenesisStateFromAppState(cdc, appGenesis)
for _, genAcc := range genesisState {
acc := genAcc.ToAccount()
if iterateFn(acc) {
break
}
}
}
//____________________________________________________________________________
// AppModuleSimulation defines the module simulation functions used by the genesis accounts module.
type AppModuleSimulation struct{}
// RegisterStoreDecoder performs a no-op.
func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {}
// GenerateGenesisState creates a randomized GenState of the genesis accounts module.
func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) {
simulation.RandomizedGenState(simState)
}
// RandomizedParams doesn't create randomized genaccounts param changes for the simulator.
func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange {
return nil
}
//____________________________________________________________________________
// AppModule implements an application module for the genesis accounts module.
type AppModule struct {
AppModuleBasic
AppModuleSimulation
accountKeeper types.AccountKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(accountKeeper types.AccountKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
AppModuleSimulation: AppModuleSimulation{},
accountKeeper: accountKeeper,
}
}
// RegisterInvariants is a placeholder function register no invariants
func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// Route empty module message route
func (AppModule) Route() string { return "" }
// NewHandler returns an empty module handler
func (AppModule) NewHandler() sdk.Handler { return nil }
// QuerierRoute returns an empty module querier route
func (AppModule) QuerierRoute() string { return "" }
// NewQuerierHandler returns an empty module querier
func (AppModule) NewQuerierHandler() sdk.Querier { return nil }
// InitGenesis performs genesis initialization for the genesis accounts module. It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, ModuleCdc, am.accountKeeper, genesisState)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the genesis accounts
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.accountKeeper)
return ModuleCdc.MustMarshalJSON(gs)
}
// BeginBlock returns an empty module begin-block
func (AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {}
// EndBlock returns an empty module end-block
func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

View File

@ -1,68 +0,0 @@
package simulation
// DONTCOVER
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// RandomizedGenState generates a random GenesisState for the genesis accounts
func RandomizedGenState(simState *module.SimulationState) {
var genesisAccounts []types.GenesisAccount
// randomly generate some genesis accounts
for i, acc := range simState.Accounts {
coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))}
bacc := authtypes.NewBaseAccountWithAddress(acc.Address)
if err := bacc.SetCoins(coins); err != nil {
panic(err)
}
var gacc types.GenesisAccount
// Only consider making a vesting account once the initial bonded validator
// set is exhausted due to needing to track DelegatedVesting.
if int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50 {
var (
vacc authexported.VestingAccount
endTime int64
)
startTime := simState.GenTimestamp.Unix()
// Allow for some vesting accounts to vest very quickly while others very slowly.
if simState.Rand.Intn(100) < 50 {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime), int(startTime+(60*60*24*30))))
} else {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime), int(startTime+(60*60*12))))
}
if startTime == endTime {
endTime++
}
if simState.Rand.Intn(100) < 50 {
vacc = authtypes.NewContinuousVestingAccount(&bacc, startTime, endTime)
} else {
vacc = authtypes.NewDelayedVestingAccount(&bacc, endTime)
}
var err error
gacc, err = types.NewGenesisAccountI(vacc)
if err != nil {
panic(err)
}
} else {
gacc = types.NewGenesisAccount(&bacc)
}
genesisAccounts = append(genesisAccounts, gacc)
}
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisAccounts)
}

View File

@ -15,10 +15,15 @@ import (
"github.com/cosmos/cosmos-sdk/version"
extypes "github.com/cosmos/cosmos-sdk/x/genutil"
v036 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_36"
v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_38"
)
// Allow applications to extend and modify the migration process.
//
// Ref: https://github.com/cosmos/cosmos-sdk/issues/5041
var migrationMap = extypes.MigrationMap{
"v0.36": v036.Migrate,
"v0.38": v038.Migrate,
}
const (

View File

@ -20,10 +20,10 @@ import (
func Migrate(appState genutil.AppMap) genutil.AppMap {
v034Codec := codec.New()
codec.RegisterCrypto(v034Codec)
v034gov.RegisterCodec(v034Codec)
v036Codec := codec.New()
codec.RegisterCrypto(v036Codec)
v034gov.RegisterCodec(v034Codec)
v036gov.RegisterCodec(v036Codec)
// migrate genesis accounts state

View File

@ -0,0 +1,32 @@
package v038
import (
"github.com/cosmos/cosmos-sdk/codec"
v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36"
v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38"
v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36"
"github.com/cosmos/cosmos-sdk/x/genutil"
)
// Migrate migrates exported state from v0.34 to a v0.36 genesis state.
func Migrate(appState genutil.AppMap) genutil.AppMap {
v036Codec := codec.New()
codec.RegisterCrypto(v036Codec)
v038Codec := codec.New()
codec.RegisterCrypto(v038Codec)
if appState[v036genaccounts.ModuleName] != nil {
var authGenState v036auth.GenesisState
v036Codec.MustUnmarshalJSON(appState[v036auth.ModuleName], &authGenState)
appState[v038auth.ModuleName] = v038Codec.MustMarshalJSON(
v038auth.Migrate(authGenState, appState[v036genaccounts.ModuleName]),
)
// delete deprecated genaccounts genesis state
delete(appState, v036genaccounts.ModuleName)
}
return appState
}

View File

@ -1,18 +1,31 @@
package types
import (
"errors"
"fmt"
"strings"
yaml "gopkg.in/yaml.v2"
"github.com/tendermint/tendermint/crypto"
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/exported"
)
var _ exported.ModuleAccountI = (*ModuleAccount)(nil)
var (
_ authexported.GenesisAccount = (*ModuleAccount)(nil)
_ exported.ModuleAccountI = (*ModuleAccount)(nil)
)
func init() {
// Register the ModuleAccount type as a GenesisAccount so that when no
// concrete GenesisAccount types exist and **default** genesis state is used,
// the genesis state will serialize correctly.
authtypes.RegisterAccountTypeCodec(&ModuleAccount{}, "cosmos-sdk/ModuleAccount")
}
// ModuleAccount defines an account for modules that holds coins on a pool
type ModuleAccount struct {
@ -26,6 +39,7 @@ func NewModuleAddress(name string) sdk.AccAddress {
return sdk.AccAddress(crypto.AddressHash([]byte(name)))
}
// NewEmptyModuleAccount creates a empty ModuleAccount from a string
func NewEmptyModuleAccount(name string, permissions ...string) *ModuleAccount {
moduleAddress := NewModuleAddress(name)
baseAcc := authtypes.NewBaseAccountWithAddress(moduleAddress)
@ -95,6 +109,18 @@ func (ma ModuleAccount) String() string {
return string(b)
}
// Validate checks for errors on the account fields
func (ma ModuleAccount) Validate() error {
if strings.TrimSpace(ma.Name) == "" {
return errors.New("module account name cannot be blank")
}
if !ma.Address.Equals(sdk.AccAddress(crypto.AddressHash([]byte(ma.Name)))) {
return fmt.Errorf("address %s cannot be derived from the module name '%s'", ma.Address, ma.Name)
}
return ma.BaseAccount.Validate()
}
// MarshalYAML returns the YAML representation of a ModuleAccount.
func (ma ModuleAccount) MarshalYAML() (interface{}, error) {
bs, err := yaml.Marshal(struct {

View File

@ -1,13 +1,18 @@
package types
import (
"errors"
"fmt"
"testing"
"github.com/tendermint/tendermint/crypto"
yaml "gopkg.in/yaml.v2"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
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/stretchr/testify/require"
)
@ -58,3 +63,35 @@ func TestHasPermissions(t *testing.T) {
}
}
}
func TestValidate(t *testing.T) {
addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
baseAcc := authtypes.NewBaseAccount(addr, sdk.Coins{}, nil, 0, 0)
tests := []struct {
name string
acc authexported.GenesisAccount
expErr error
}{
{
"valid module account",
NewEmptyModuleAccount("test"),
nil,
},
{
"invalid name and address pair",
NewModuleAccount(baseAcc, "test"),
fmt.Errorf("address %s cannot be derived from the module name 'test'", addr),
},
{
"empty module account name",
NewModuleAccount(baseAcc, " "),
errors.New("module account name cannot be blank"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.acc.Validate()
require.Equal(t, tt.expErr, err)
})
}
}