Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions tests/integration/distribution/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package distribution_test

import (
"encoding/json"
"testing"

corestore "cosmossdk.io/core/store"
coretesting "cosmossdk.io/core/testing"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
sdkmath "cosmossdk.io/math"
"cosmossdk.io/x/distribution/keeper"
"cosmossdk.io/x/distribution/types"
stakingkeeper "cosmossdk.io/x/staking/keeper"
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"

bankkeeper "cosmossdk.io/x/bank/keeper"
_ "github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
)

type ImportExportSuite struct {
suite.Suite

cdc codec.Codec
app *runtime.App
addrs []sdk.AccAddress
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
DistributionKeeper keeper.Keeper
StakingKeeper *stakingkeeper.Keeper
appBuilder *runtime.AppBuilder
}

func TestDistributionImportExport(t *testing.T) {
suite.Run(t, new(ImportExportSuite))
}

func (s *ImportExportSuite) SetupTest() {
var err error
valTokens := sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction)
s.app, err = simtestutil.SetupWithConfiguration(
depinject.Configs(
AppConfig,
depinject.Supply(log.NewNopLogger()),
),
simtestutil.DefaultStartUpConfig(),
&s.AccountKeeper, &s.BankKeeper, &s.DistributionKeeper, &s.StakingKeeper,
&s.cdc, &s.appBuilder,
)
s.Require().NoError(err)

ctx := s.app.BaseApp.NewContext(false)
s.addrs = simtestutil.AddTestAddrs(s.BankKeeper, s.StakingKeeper, ctx, 1, valTokens)

_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
Height: s.app.LastBlockHeight() + 1,
})
s.Require().NoError(err)
}

func (s *ImportExportSuite) TestHappyPath() {
ctx := s.app.NewContext(true)
// Imagine a situation where rewards were, e.g. 100 / 3 = 33, but the fee collector sent 100 to the distribution module.
// There're 99 tokens in rewards, but 100 in the module; let's simulate a situation where there are 34 tokens left in the module,
// and a single validator has 33 tokens of rewards.
rewards := sdk.NewDecCoinsFromCoins(sdk.NewCoin("stake", sdkmath.NewInt(33)))

// We'll pretend s.addrs[0] is the fee collector module.
err := s.BankKeeper.SendCoinsFromAccountToModule(ctx, s.addrs[0], types.ModuleName, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(34))))
s.Require().NoError(err)

validators, err := s.StakingKeeper.GetAllValidators(ctx)
s.Require().NoError(err)
val := validators[0]

err = s.DistributionKeeper.AllocateTokensToValidator(ctx, val, rewards)
s.Require().NoError(err)

_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
Height: s.app.LastBlockHeight() + 1,
})
s.Require().NoError(err)

valBz, err := s.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
s.Require().NoError(err)
outstanding, err := s.DistributionKeeper.ValidatorOutstandingRewards.Get(ctx, valBz)
s.Require().NoError(err)
s.Require().Equal(rewards, outstanding.Rewards)

genesisState, err := s.app.ModuleManager.ExportGenesis(ctx)
s.Require().NoError(err)
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
s.Require().NoError(err)

db := coretesting.NewMemDB()
conf2 := simtestutil.DefaultStartUpConfig()
conf2.DB = db
app2, err := simtestutil.SetupWithConfiguration(
depinject.Configs(
AppConfig,
depinject.Supply(log.NewNopLogger()),
),
conf2,
)
s.Require().NoError(err)

s.clearDB(db)
err = app2.CommitMultiStore().LoadLatestVersion()
s.Require().NoError(err)

_, err = app2.InitChain(
&abci.InitChainRequest{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simtestutil.DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
s.Require().NoError(err)
}

func (s *ImportExportSuite) TestInsufficientFunds() {
ctx := s.app.NewContext(true)
rewards := sdk.NewCoin("stake", sdkmath.NewInt(35))

validators, err := s.StakingKeeper.GetAllValidators(ctx)
s.Require().NoError(err)

err = s.DistributionKeeper.AllocateTokensToValidator(ctx, validators[0], sdk.NewDecCoinsFromCoins(rewards))
s.Require().NoError(err)

// We'll pretend s.addrs[0] is the fee collector module.
err = s.BankKeeper.SendCoinsFromAccountToModule(ctx, s.addrs[0], types.ModuleName, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(34))))
s.Require().NoError(err)

_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
Height: s.app.LastBlockHeight() + 1,
})
s.Require().NoError(err)

genesisState, err := s.app.ModuleManager.ExportGenesis(ctx)
s.Require().NoError(err)
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
s.Require().NoError(err)

db := coretesting.NewMemDB()
conf2 := simtestutil.DefaultStartUpConfig()
conf2.DB = db
app2, err := simtestutil.SetupWithConfiguration(
depinject.Configs(
AppConfig,
depinject.Supply(log.NewNopLogger()),
),
conf2,
)
s.Require().NoError(err)

s.clearDB(db)
err = app2.CommitMultiStore().LoadLatestVersion()
s.Require().NoError(err)

_, err = app2.InitChain(
&abci.InitChainRequest{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simtestutil.DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
s.Require().ErrorContains(err, "distribution module balance is less than module holdings")
}

func (s *ImportExportSuite) clearDB(db corestore.KVStoreWithBatch) {
iter, err := db.Iterator(nil, nil)
s.Require().NoError(err)
defer iter.Close()

var keys [][]byte
for ; iter.Valid(); iter.Next() {
keys = append(keys, iter.Key())
}

for _, k := range keys {
s.Require().NoError(db.Delete(k))
}
}
4 changes: 4 additions & 0 deletions x/distribution/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### Improvements

* [#22832](https://github.com/cosmos/cosmos-sdk/pull/22832) Ensure the distribution module has at least as many tokens as outstanding rewards at genesis import

## [v0.2.0-rc.1](https://github.com/cosmos/cosmos-sdk/releases/tag/x/distribution/v0.2.0-rc.1) - 2024-12-18

### Improvements
Expand Down
4 changes: 2 additions & 2 deletions x/distribution/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ func (k Keeper) InitGenesis(ctx context.Context, data types.GenesisState) error
if balances.IsZero() {
k.authKeeper.SetModuleAccount(ctx, moduleAcc)
}
if !balances.Equal(moduleHoldingsInt) {
return fmt.Errorf("distribution module balance does not match the module holdings: %s <-> %s", balances, moduleHoldingsInt)
if balances.IsAllLT(moduleHoldingsInt) {
return fmt.Errorf("distribution module balance is less than module holdings: %s < %s", balances, moduleHoldingsInt)
}
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions x/gov/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### Improvements

* [#22832](https://github.com/cosmos/cosmos-sdk/pull/22832) Ensure the governance module has at least as many tokens as are deposited at genesis import.

## [v0.2.0-rc.1](https://github.com/cosmos/cosmos-sdk/releases/tag/x/gov/v0.2.0-rc.1) - 2024-12-18

### Features
Expand Down
6 changes: 3 additions & 3 deletions x/gov/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ func InitGenesis(ctx context.Context, ak types.AccountKeeper, bk types.BankKeepe
ak.SetModuleAccount(ctx, moduleAcc)
}

// check if total deposits equals balance, if it doesn't return an error
if !balance.Equal(totalDeposits) {
return fmt.Errorf("expected module account was %s but we got %s", balance.String(), totalDeposits.String())
// check if the module account can cover the total deposits
if !balance.IsAllGTE(totalDeposits) {
return fmt.Errorf("expected gov module to hold at least %s, but it holds %s", totalDeposits, balance)
}
return nil
}
Expand Down
Loading