fix(x/gov,x/distribution): Balance assertions on genesis import shouldn't be exact (backport #22832) (#23194)

Co-authored-by: violet <158512193+fastfadingviolets@users.noreply.github.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2025-01-06 12:34:07 +01:00 committed by GitHub
parent 394c22a860
commit 95c44e587a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 203 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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