refactor: move x/staking Init/Export genesis to keeper (#11866)

This commit is contained in:
Aleksandr Bezobchuk 2022-05-04 09:50:58 -04:00 committed by GitHub
parent 35d3312c3b
commit 35c81467cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 429 additions and 404 deletions

View File

@ -17,7 +17,6 @@ import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
@ -181,7 +180,7 @@ func (suite *GenTxTestSuite) TestValidateAccountInGenesis() {
cdc := suite.encodingConfig.Codec
suite.app.StakingKeeper.SetParams(suite.ctx, stakingtypes.DefaultParams())
stakingGenesisState := staking.ExportGenesis(suite.ctx, suite.app.StakingKeeper)
stakingGenesisState := suite.app.StakingKeeper.ExportGenesis(suite.ctx)
suite.Require().Equal(stakingGenesisState.Params, stakingtypes.DefaultParams())
stakingGenesis, err := cdc.MarshalJSON(stakingGenesisState) // TODO switch this to use Marshaler
suite.Require().NoError(err)

View File

@ -19,7 +19,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
@ -56,7 +55,7 @@ func TestImportExportQueues(t *testing.T) {
authGenState := auth.ExportGenesis(ctx, app.AccountKeeper)
bankGenState := app.BankKeeper.ExportGenesis(ctx)
stakingGenState := staking.ExportGenesis(ctx, app.StakingKeeper)
stakingGenState := app.StakingKeeper.ExportGenesis(ctx)
distributionGenState := app.DistrKeeper.ExportGenesis(ctx)
// export the state and import it into a new app

View File

@ -3,7 +3,6 @@ package staking
import (
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
@ -12,201 +11,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// InitGenesis sets the pool and parameters for the provided keeper. For each
// validator in data, it sets that validator in the keeper along with manually
// setting the indexes. In addition, it also sets any delegations found in
// data. Finally, it updates the bonded validators.
// Returns final validator set after applying all declaration and delegations
func InitGenesis(
ctx sdk.Context, keeper keeper.Keeper, accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper, data *types.GenesisState,
) (res []abci.ValidatorUpdate) {
bondedTokens := sdk.ZeroInt()
notBondedTokens := sdk.ZeroInt()
// We need to pretend to be "n blocks before genesis", where "n" is the
// validator update delay, so that e.g. slashing periods are correctly
// initialized for the validator set e.g. with a one-block offset - the
// first TM block is at height 1, so state updates applied from
// genesis.json are in block 0.
ctx = ctx.WithBlockHeight(1 - sdk.ValidatorUpdateDelay)
keeper.SetParams(ctx, data.Params)
keeper.SetLastTotalPower(ctx, data.LastTotalPower)
for _, validator := range data.Validators {
keeper.SetValidator(ctx, validator)
// Manually set indices for the first time
keeper.SetValidatorByConsAddr(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator)
// Call the creation hook if not exported
if !data.Exported {
if err := keeper.AfterValidatorCreated(ctx, validator.GetOperator()); err != nil {
panic(err)
}
}
// update timeslice if necessary
if validator.IsUnbonding() {
keeper.InsertUnbondingValidatorQueue(ctx, validator)
}
switch validator.GetStatus() {
case types.Bonded:
bondedTokens = bondedTokens.Add(validator.GetTokens())
case types.Unbonding, types.Unbonded:
notBondedTokens = notBondedTokens.Add(validator.GetTokens())
default:
panic("invalid validator status")
}
}
for _, delegation := range data.Delegations {
delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
if err != nil {
panic(err)
}
// Call the before-creation hook if not exported
if !data.Exported {
if err := keeper.BeforeDelegationCreated(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
keeper.SetDelegation(ctx, delegation)
// Call the after-modification hook if not exported
if !data.Exported {
if err := keeper.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
}
for _, ubd := range data.UnbondingDelegations {
keeper.SetUnbondingDelegation(ctx, ubd)
for _, entry := range ubd.Entries {
keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime)
notBondedTokens = notBondedTokens.Add(entry.Balance)
}
}
for _, red := range data.Redelegations {
keeper.SetRedelegation(ctx, red)
for _, entry := range red.Entries {
keeper.InsertRedelegationQueue(ctx, red, entry.CompletionTime)
}
}
bondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, bondedTokens))
notBondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, notBondedTokens))
// check if the unbonded and bonded pools accounts exists
bondedPool := keeper.GetBondedPool(ctx)
if bondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName))
}
// TODO: remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
bondedBalance := bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
if bondedBalance.IsZero() {
accountKeeper.SetModuleAccount(ctx, bondedPool)
}
// if balance is different from bonded coins panic because genesis is most likely malformed
if !bondedBalance.IsEqual(bondedCoins) {
panic(fmt.Sprintf("bonded pool balance is different from bonded coins: %s <-> %s", bondedBalance, bondedCoins))
}
notBondedPool := keeper.GetNotBondedPool(ctx)
if notBondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName))
}
notBondedBalance := bankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
if notBondedBalance.IsZero() {
accountKeeper.SetModuleAccount(ctx, notBondedPool)
}
// If balance is different from non bonded coins panic because genesis is most
// likely malformed.
if !notBondedBalance.IsEqual(notBondedCoins) {
panic(fmt.Sprintf("not bonded pool balance is different from not bonded coins: %s <-> %s", notBondedBalance, notBondedCoins))
}
// don't need to run Tendermint updates if we exported
if data.Exported {
for _, lv := range data.LastValidatorPowers {
valAddr, err := sdk.ValAddressFromBech32(lv.Address)
if err != nil {
panic(err)
}
keeper.SetLastValidatorPower(ctx, valAddr, lv.Power)
validator, found := keeper.GetValidator(ctx, valAddr)
if !found {
panic(fmt.Sprintf("validator %s not found", lv.Address))
}
update := validator.ABCIValidatorUpdate(keeper.PowerReduction(ctx))
update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block
res = append(res, update)
}
} else {
var err error
res, err = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
panic(err)
}
}
return res
}
// ExportGenesis returns a GenesisState for a given context and keeper. The
// GenesisState will contain the pool, params, validators, and bonds found in
// the keeper.
func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState {
var unbondingDelegations []types.UnbondingDelegation
keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) {
unbondingDelegations = append(unbondingDelegations, ubd)
return false
})
var redelegations []types.Redelegation
keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) {
redelegations = append(redelegations, red)
return false
})
var lastValidatorPowers []types.LastValidatorPower
keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) {
lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{Address: addr.String(), Power: power})
return false
})
return &types.GenesisState{
Params: keeper.GetParams(ctx),
LastTotalPower: keeper.GetLastTotalPower(ctx),
LastValidatorPowers: lastValidatorPowers,
Validators: keeper.GetAllValidators(ctx),
Delegations: keeper.GetAllDelegations(ctx),
UnbondingDelegations: unbondingDelegations,
Redelegations: redelegations,
Exported: true,
}
}
// WriteValidators returns a slice of bonded genesis validators.
func WriteValidators(ctx sdk.Context, keeper keeper.Keeper) (vals []tmtypes.GenesisValidator, err error) {
keeper.IterateLastValidators(ctx, func(_ int64, validator types.ValidatorI) (stop bool) {

View File

@ -1,218 +1,17 @@
package staking_test
import (
"fmt"
"log"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func bootstrapGenesisTest(t *testing.T, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress) {
_, app, ctx := getBaseSimappWithCustomKeeper(t)
addrDels, _ := generateAddresses(app, ctx, numAddrs, sdk.NewInt(10000))
return app, ctx, addrDels
}
func TestInitGenesis(t *testing.T) {
app, ctx, addrs := bootstrapGenesisTest(t, 10)
valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
params := app.StakingKeeper.GetParams(ctx)
validators := app.StakingKeeper.GetAllValidators(ctx)
require.Len(t, validators, 1)
var delegations []types.Delegation
pk0, err := codectypes.NewAnyWithValue(PKs[0])
require.NoError(t, err)
pk1, err := codectypes.NewAnyWithValue(PKs[1])
require.NoError(t, err)
// initialize the validators
bondedVal1 := types.Validator{
OperatorAddress: sdk.ValAddress(addrs[0]).String(),
ConsensusPubkey: pk0,
Status: types.Bonded,
Tokens: valTokens,
DelegatorShares: sdk.NewDecFromInt(valTokens),
Description: types.NewDescription("hoop", "", "", "", ""),
}
bondedVal2 := types.Validator{
OperatorAddress: sdk.ValAddress(addrs[1]).String(),
ConsensusPubkey: pk1,
Status: types.Bonded,
Tokens: valTokens,
DelegatorShares: sdk.NewDecFromInt(valTokens),
Description: types.NewDescription("bloop", "", "", "", ""),
}
// append new bonded validators to the list
validators = append(validators, bondedVal1, bondedVal2)
log.Printf("%#v", len(validators))
// mint coins in the bonded pool representing the validators coins
i2 := len(validators) - 1 // -1 to exclude genesis validator
require.NoError(t,
testutil.FundModuleAccount(
app.BankKeeper,
ctx,
types.BondedPoolName,
sdk.NewCoins(
sdk.NewCoin(params.BondDenom, valTokens.MulRaw((int64)(i2))),
),
),
)
genesisDelegations := app.StakingKeeper.GetAllDelegations(ctx)
delegations = append(delegations, genesisDelegations...)
genesisState := types.NewGenesisState(params, validators, delegations)
vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState)
actualGenesis := staking.ExportGenesis(ctx, app.StakingKeeper)
require.Equal(t, genesisState.Params, actualGenesis.Params)
require.Equal(t, genesisState.Delegations, actualGenesis.Delegations)
require.EqualValues(t, app.StakingKeeper.GetAllValidators(ctx), actualGenesis.Validators)
// Ensure validators have addresses.
vals2, err := staking.WriteValidators(ctx, app.StakingKeeper)
require.NoError(t, err)
for _, val := range vals2 {
require.NotEmpty(t, val.Address)
}
// now make sure the validators are bonded and intra-tx counters are correct
resVal, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0]))
require.True(t, found)
require.Equal(t, types.Bonded, resVal.Status)
resVal, found = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1]))
require.True(t, found)
require.Equal(t, types.Bonded, resVal.Status)
abcivals := make([]abci.ValidatorUpdate, len(vals))
validators = validators[1:] // remove genesis validator
for i, val := range validators {
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
}
require.Equal(t, abcivals, vals)
}
func TestInitGenesis_PoolsBalanceMismatch(t *testing.T) {
app := simapp.Setup(t, false)
ctx := app.NewContext(false, tmproto.Header{})
consPub, err := codectypes.NewAnyWithValue(PKs[0])
require.NoError(t, err)
// create mock validator
validator := types.Validator{
OperatorAddress: sdk.ValAddress("12345678901234567890").String(),
ConsensusPubkey: consPub,
Jailed: false,
Tokens: sdk.NewInt(10),
DelegatorShares: sdk.NewDecFromInt(sdk.NewInt(10)),
Description: types.NewDescription("bloop", "", "", "", ""),
}
// valid params
params := types.Params{
UnbondingTime: 10000,
MaxValidators: 1,
MaxEntries: 10,
BondDenom: "stake",
}
// test
require.Panics(t, func() {
// setting validator status to bonded so the balance counts towards bonded pool
validator.Status = types.Bonded
staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, &types.GenesisState{
Params: params,
Validators: []types.Validator{validator},
})
}, "should panic because bonded pool balance is different from bonded pool coins")
require.Panics(t, func() {
// setting validator status to unbonded so the balance counts towards not bonded pool
validator.Status = types.Unbonded
staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, &types.GenesisState{
Params: params,
Validators: []types.Validator{validator},
})
}, "should panic because not bonded pool balance is different from not bonded pool coins")
}
func TestInitGenesisLargeValidatorSet(t *testing.T) {
size := 200
require.True(t, size > 100)
app, ctx, addrs := bootstrapGenesisTest(t, 200)
genesisValidators := app.StakingKeeper.GetAllValidators(ctx)
params := app.StakingKeeper.GetParams(ctx)
delegations := []types.Delegation{}
validators := make([]types.Validator, size)
var err error
bondedPoolAmt := sdk.ZeroInt()
for i := range validators {
validators[i], err = types.NewValidator(sdk.ValAddress(addrs[i]),
PKs[i], types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", ""))
require.NoError(t, err)
validators[i].Status = types.Bonded
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
if i < 100 {
tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
}
validators[i].Tokens = tokens
validators[i].DelegatorShares = sdk.NewDecFromInt(tokens)
// add bonded coins
bondedPoolAmt = bondedPoolAmt.Add(tokens)
}
validators = append(validators, genesisValidators...)
genesisState := types.NewGenesisState(params, validators, delegations)
// mint coins in the bonded pool representing the validators coins
require.NoError(t,
testutil.FundModuleAccount(
app.BankKeeper,
ctx,
types.BondedPoolName,
sdk.NewCoins(sdk.NewCoin(params.BondDenom, bondedPoolAmt)),
),
)
vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState)
abcivals := make([]abci.ValidatorUpdate, 100)
for i, val := range validators[:100] {
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
}
// remove genesis validator
vals = vals[:100]
require.Equal(t, abcivals, vals)
}
func TestValidateGenesis(t *testing.T) {
genValidators1 := make([]types.Validator, 1, 5)
pk := ed25519.GenPrivKey().PubKey()
@ -244,9 +43,11 @@ func TestValidateGenesis(t *testing.T) {
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
genesisState := types.DefaultGenesisState()
tt.mutate(genesisState)
if tt.wantErr {
assert.Error(t, staking.ValidateGenesis(genesisState))
} else {

204
x/staking/keeper/genesis.go Normal file
View File

@ -0,0 +1,204 @@
package keeper
import (
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// InitGenesis sets the pool and parameters for the provided keeper. For each
// validator in data, it sets that validator in the keeper along with manually
// setting the indexes. In addition, it also sets any delegations found in
// data. Finally, it updates the bonded validators.
// Returns final validator set after applying all declaration and delegations
func (k Keeper) InitGenesis(ctx sdk.Context, data *types.GenesisState) (res []abci.ValidatorUpdate) {
bondedTokens := sdk.ZeroInt()
notBondedTokens := sdk.ZeroInt()
// We need to pretend to be "n blocks before genesis", where "n" is the
// validator update delay, so that e.g. slashing periods are correctly
// initialized for the validator set e.g. with a one-block offset - the
// first TM block is at height 1, so state updates applied from
// genesis.json are in block 0.
ctx = ctx.WithBlockHeight(1 - sdk.ValidatorUpdateDelay)
k.SetParams(ctx, data.Params)
k.SetLastTotalPower(ctx, data.LastTotalPower)
for _, validator := range data.Validators {
k.SetValidator(ctx, validator)
// Manually set indices for the first time
k.SetValidatorByConsAddr(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator)
// Call the creation hook if not exported
if !data.Exported {
if err := k.AfterValidatorCreated(ctx, validator.GetOperator()); err != nil {
panic(err)
}
}
// update timeslice if necessary
if validator.IsUnbonding() {
k.InsertUnbondingValidatorQueue(ctx, validator)
}
switch validator.GetStatus() {
case types.Bonded:
bondedTokens = bondedTokens.Add(validator.GetTokens())
case types.Unbonding, types.Unbonded:
notBondedTokens = notBondedTokens.Add(validator.GetTokens())
default:
panic("invalid validator status")
}
}
for _, delegation := range data.Delegations {
delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
if err != nil {
panic(err)
}
// Call the before-creation hook if not exported
if !data.Exported {
if err := k.BeforeDelegationCreated(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
k.SetDelegation(ctx, delegation)
// Call the after-modification hook if not exported
if !data.Exported {
if err := k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
}
for _, ubd := range data.UnbondingDelegations {
k.SetUnbondingDelegation(ctx, ubd)
for _, entry := range ubd.Entries {
k.InsertUBDQueue(ctx, ubd, entry.CompletionTime)
notBondedTokens = notBondedTokens.Add(entry.Balance)
}
}
for _, red := range data.Redelegations {
k.SetRedelegation(ctx, red)
for _, entry := range red.Entries {
k.InsertRedelegationQueue(ctx, red, entry.CompletionTime)
}
}
bondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, bondedTokens))
notBondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, notBondedTokens))
// check if the unbonded and bonded pools accounts exists
bondedPool := k.GetBondedPool(ctx)
if bondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName))
}
// TODO: remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
bondedBalance := k.bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
if bondedBalance.IsZero() {
k.authKeeper.SetModuleAccount(ctx, bondedPool)
}
// if balance is different from bonded coins panic because genesis is most likely malformed
if !bondedBalance.IsEqual(bondedCoins) {
panic(fmt.Sprintf("bonded pool balance is different from bonded coins: %s <-> %s", bondedBalance, bondedCoins))
}
notBondedPool := k.GetNotBondedPool(ctx)
if notBondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName))
}
notBondedBalance := k.bankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
if notBondedBalance.IsZero() {
k.authKeeper.SetModuleAccount(ctx, notBondedPool)
}
// If balance is different from non bonded coins panic because genesis is most
// likely malformed.
if !notBondedBalance.IsEqual(notBondedCoins) {
panic(fmt.Sprintf("not bonded pool balance is different from not bonded coins: %s <-> %s", notBondedBalance, notBondedCoins))
}
// don't need to run Tendermint updates if we exported
if data.Exported {
for _, lv := range data.LastValidatorPowers {
valAddr, err := sdk.ValAddressFromBech32(lv.Address)
if err != nil {
panic(err)
}
k.SetLastValidatorPower(ctx, valAddr, lv.Power)
validator, found := k.GetValidator(ctx, valAddr)
if !found {
panic(fmt.Sprintf("validator %s not found", lv.Address))
}
update := validator.ABCIValidatorUpdate(k.PowerReduction(ctx))
update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block
res = append(res, update)
}
} else {
var err error
res, err = k.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
panic(err)
}
}
return res
}
// ExportGenesis returns a GenesisState for a given context and keeper. The
// GenesisState will contain the pool, params, validators, and bonds found in
// the keeper.
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
var unbondingDelegations []types.UnbondingDelegation
k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) {
unbondingDelegations = append(unbondingDelegations, ubd)
return false
})
var redelegations []types.Redelegation
k.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) {
redelegations = append(redelegations, red)
return false
})
var lastValidatorPowers []types.LastValidatorPower
k.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) {
lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{Address: addr.String(), Power: power})
return false
})
return &types.GenesisState{
Params: k.GetParams(ctx),
LastTotalPower: k.GetLastTotalPower(ctx),
LastValidatorPowers: lastValidatorPowers,
Validators: k.GetAllValidators(ctx),
Delegations: k.GetAllDelegations(ctx),
UnbondingDelegations: unbondingDelegations,
Redelegations: redelegations,
Exported: true,
}
}

View File

@ -0,0 +1,219 @@
package keeper_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func bootstrapGenesisTest(t *testing.T, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress) {
app := simapp.Setup(t, false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
addrDels, _ := generateAddresses(app, ctx, numAddrs)
return app, ctx, addrDels
}
func TestInitGenesis(t *testing.T) {
app, ctx, addrs := bootstrapGenesisTest(t, 10)
valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
params := app.StakingKeeper.GetParams(ctx)
validators := app.StakingKeeper.GetAllValidators(ctx)
require.Len(t, validators, 1)
var delegations []types.Delegation
pk0, err := codectypes.NewAnyWithValue(PKs[0])
require.NoError(t, err)
pk1, err := codectypes.NewAnyWithValue(PKs[1])
require.NoError(t, err)
// initialize the validators
bondedVal1 := types.Validator{
OperatorAddress: sdk.ValAddress(addrs[0]).String(),
ConsensusPubkey: pk0,
Status: types.Bonded,
Tokens: valTokens,
DelegatorShares: sdk.NewDecFromInt(valTokens),
Description: types.NewDescription("hoop", "", "", "", ""),
}
bondedVal2 := types.Validator{
OperatorAddress: sdk.ValAddress(addrs[1]).String(),
ConsensusPubkey: pk1,
Status: types.Bonded,
Tokens: valTokens,
DelegatorShares: sdk.NewDecFromInt(valTokens),
Description: types.NewDescription("bloop", "", "", "", ""),
}
// append new bonded validators to the list
validators = append(validators, bondedVal1, bondedVal2)
// mint coins in the bonded pool representing the validators coins
i2 := len(validators) - 1 // -1 to exclude genesis validator
require.NoError(t,
testutil.FundModuleAccount(
app.BankKeeper,
ctx,
types.BondedPoolName,
sdk.NewCoins(
sdk.NewCoin(params.BondDenom, valTokens.MulRaw((int64)(i2))),
),
),
)
genesisDelegations := app.StakingKeeper.GetAllDelegations(ctx)
delegations = append(delegations, genesisDelegations...)
genesisState := types.NewGenesisState(params, validators, delegations)
vals := app.StakingKeeper.InitGenesis(ctx, genesisState)
actualGenesis := app.StakingKeeper.ExportGenesis(ctx)
require.Equal(t, genesisState.Params, actualGenesis.Params)
require.Equal(t, genesisState.Delegations, actualGenesis.Delegations)
require.EqualValues(t, app.StakingKeeper.GetAllValidators(ctx), actualGenesis.Validators)
// Ensure validators have addresses.
vals2, err := staking.WriteValidators(ctx, app.StakingKeeper)
require.NoError(t, err)
for _, val := range vals2 {
require.NotEmpty(t, val.Address)
}
// now make sure the validators are bonded and intra-tx counters are correct
resVal, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0]))
require.True(t, found)
require.Equal(t, types.Bonded, resVal.Status)
resVal, found = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1]))
require.True(t, found)
require.Equal(t, types.Bonded, resVal.Status)
abcivals := make([]abci.ValidatorUpdate, len(vals))
validators = validators[1:] // remove genesis validator
for i, val := range validators {
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
}
require.Equal(t, abcivals, vals)
}
func TestInitGenesis_PoolsBalanceMismatch(t *testing.T) {
app := simapp.Setup(t, false)
ctx := app.NewContext(false, tmproto.Header{})
consPub, err := codectypes.NewAnyWithValue(PKs[0])
require.NoError(t, err)
validator := types.Validator{
OperatorAddress: sdk.ValAddress("12345678901234567890").String(),
ConsensusPubkey: consPub,
Jailed: false,
Tokens: sdk.NewInt(10),
DelegatorShares: sdk.NewDecFromInt(sdk.NewInt(10)),
Description: types.NewDescription("bloop", "", "", "", ""),
}
params := types.Params{
UnbondingTime: 10000,
MaxValidators: 1,
MaxEntries: 10,
BondDenom: "stake",
}
require.Panics(t, func() {
// setting validator status to bonded so the balance counts towards bonded pool
validator.Status = types.Bonded
app.StakingKeeper.InitGenesis(ctx, &types.GenesisState{
Params: params,
Validators: []types.Validator{validator},
})
},
"should panic because bonded pool balance is different from bonded pool coins",
)
require.Panics(t, func() {
// setting validator status to unbonded so the balance counts towards not bonded pool
validator.Status = types.Unbonded
app.StakingKeeper.InitGenesis(ctx, &types.GenesisState{
Params: params,
Validators: []types.Validator{validator},
})
},
"should panic because not bonded pool balance is different from not bonded pool coins",
)
}
func TestInitGenesisLargeValidatorSet(t *testing.T) {
size := 200
require.True(t, size > 100)
app, ctx, addrs := bootstrapGenesisTest(t, 200)
genesisValidators := app.StakingKeeper.GetAllValidators(ctx)
params := app.StakingKeeper.GetParams(ctx)
delegations := []types.Delegation{}
validators := make([]types.Validator, size)
var err error
bondedPoolAmt := sdk.ZeroInt()
for i := range validators {
validators[i], err = types.NewValidator(
sdk.ValAddress(addrs[i]),
PKs[i],
types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", ""),
)
require.NoError(t, err)
validators[i].Status = types.Bonded
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
if i < 100 {
tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
}
validators[i].Tokens = tokens
validators[i].DelegatorShares = sdk.NewDecFromInt(tokens)
// add bonded coins
bondedPoolAmt = bondedPoolAmt.Add(tokens)
}
validators = append(validators, genesisValidators...)
genesisState := types.NewGenesisState(params, validators, delegations)
// mint coins in the bonded pool representing the validators coins
require.NoError(t,
testutil.FundModuleAccount(
app.BankKeeper,
ctx,
types.BondedPoolName,
sdk.NewCoins(sdk.NewCoin(params.BondDenom, bondedPoolAmt)),
),
)
vals := app.StakingKeeper.InitGenesis(ctx, genesisState)
abcivals := make([]abci.ValidatorUpdate, 100)
for i, val := range validators[:100] {
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
}
// remove genesis validator
vals = vals[:100]
require.Equal(t, abcivals, vals)
}

View File

@ -149,14 +149,13 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.
cdc.MustUnmarshalJSON(data, &genesisState)
return InitGenesis(ctx, am.keeper, am.accountKeeper, am.bankKeeper, &genesisState)
return am.keeper.InitGenesis(ctx, &genesisState)
}
// ExportGenesis returns the exported genesis state as raw bytes for the staking
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return cdc.MustMarshalJSON(gs)
return cdc.MustMarshalJSON(am.keeper.ExportGenesis(ctx))
}
// ConsensusVersion implements AppModule/ConsensusVersion.