Skip to content

Commit 5534e4f

Browse files
ci: tests for distribution,gov module balances at genesis
1 parent 882b546 commit 5534e4f

File tree

3 files changed

+284
-2
lines changed

3 files changed

+284
-2
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package distribution_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
corestore "cosmossdk.io/core/store"
8+
coretesting "cosmossdk.io/core/testing"
9+
"cosmossdk.io/depinject"
10+
"cosmossdk.io/log"
11+
sdkmath "cosmossdk.io/math"
12+
"cosmossdk.io/x/distribution/keeper"
13+
"cosmossdk.io/x/distribution/types"
14+
stakingkeeper "cosmossdk.io/x/staking/keeper"
15+
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
16+
"github.com/cosmos/cosmos-sdk/codec"
17+
"github.com/cosmos/cosmos-sdk/runtime"
18+
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
19+
sdk "github.com/cosmos/cosmos-sdk/types"
20+
"github.com/stretchr/testify/suite"
21+
22+
bankkeeper "cosmossdk.io/x/bank/keeper"
23+
_ "github.com/cosmos/cosmos-sdk/x/auth"
24+
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
25+
)
26+
27+
type ImportExportSuite struct {
28+
suite.Suite
29+
30+
cdc codec.Codec
31+
app *runtime.App
32+
addrs []sdk.AccAddress
33+
AccountKeeper authkeeper.AccountKeeper
34+
BankKeeper bankkeeper.Keeper
35+
DistributionKeeper keeper.Keeper
36+
StakingKeeper *stakingkeeper.Keeper
37+
appBuilder *runtime.AppBuilder
38+
}
39+
40+
func TestDistributionImportExport(t *testing.T) {
41+
suite.Run(t, new(ImportExportSuite))
42+
}
43+
44+
func (s *ImportExportSuite) SetupTest() {
45+
var err error
46+
valTokens := sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction)
47+
s.app, err = simtestutil.SetupWithConfiguration(
48+
depinject.Configs(
49+
AppConfig,
50+
depinject.Supply(log.NewNopLogger()),
51+
),
52+
simtestutil.DefaultStartUpConfig(),
53+
&s.AccountKeeper, &s.BankKeeper, &s.DistributionKeeper, &s.StakingKeeper,
54+
&s.cdc, &s.appBuilder,
55+
)
56+
s.Require().NoError(err)
57+
58+
ctx := s.app.BaseApp.NewContext(false)
59+
s.addrs = simtestutil.AddTestAddrs(s.BankKeeper, s.StakingKeeper, ctx, 1, valTokens)
60+
61+
_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
62+
Height: s.app.LastBlockHeight() + 1,
63+
})
64+
s.Require().NoError(err)
65+
}
66+
67+
func (s *ImportExportSuite) TestHappyPath() {
68+
ctx := s.app.NewContext(true)
69+
// Imagine a situation where rewards were, e.g. 100 / 3 = 33, but the fee collector sent 100 to the distribution module.
70+
// 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,
71+
// and a single validator has 33 tokens of rewards.
72+
rewards := sdk.NewDecCoinsFromCoins(sdk.NewCoin("stake", sdkmath.NewInt(33)))
73+
74+
// We'll pretend s.addrs[0] is the fee collector module.
75+
err := s.BankKeeper.SendCoinsFromAccountToModule(ctx, s.addrs[0], types.ModuleName, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(34))))
76+
s.Require().NoError(err)
77+
78+
validators, err := s.StakingKeeper.GetAllValidators(ctx)
79+
s.Require().NoError(err)
80+
val := validators[0]
81+
82+
err = s.DistributionKeeper.AllocateTokensToValidator(ctx, val, rewards)
83+
s.Require().NoError(err)
84+
85+
_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
86+
Height: s.app.LastBlockHeight() + 1,
87+
})
88+
s.Require().NoError(err)
89+
90+
valBz, err := s.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
91+
s.Require().NoError(err)
92+
outstanding, err := s.DistributionKeeper.ValidatorOutstandingRewards.Get(ctx, valBz)
93+
s.Require().NoError(err)
94+
s.Require().Equal(rewards, outstanding.Rewards)
95+
96+
genesisState, err := s.app.ModuleManager.ExportGenesis(ctx)
97+
s.Require().NoError(err)
98+
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
99+
s.Require().NoError(err)
100+
101+
db := coretesting.NewMemDB()
102+
conf2 := simtestutil.DefaultStartUpConfig()
103+
conf2.DB = db
104+
app2, err := simtestutil.SetupWithConfiguration(
105+
depinject.Configs(
106+
AppConfig,
107+
depinject.Supply(log.NewNopLogger()),
108+
),
109+
conf2,
110+
)
111+
s.Require().NoError(err)
112+
113+
s.clearDB(db)
114+
err = app2.CommitMultiStore().LoadLatestVersion()
115+
s.Require().NoError(err)
116+
117+
_, err = app2.InitChain(
118+
&abci.InitChainRequest{
119+
Validators: []abci.ValidatorUpdate{},
120+
ConsensusParams: simtestutil.DefaultConsensusParams,
121+
AppStateBytes: stateBytes,
122+
},
123+
)
124+
s.Require().NoError(err)
125+
}
126+
127+
func (s *ImportExportSuite) TestInsufficientFunds() {
128+
ctx := s.app.NewContext(true)
129+
rewards := sdk.NewCoin("stake", sdkmath.NewInt(35))
130+
131+
validators, err := s.StakingKeeper.GetAllValidators(ctx)
132+
s.Require().NoError(err)
133+
134+
err = s.DistributionKeeper.AllocateTokensToValidator(ctx, validators[0], sdk.NewDecCoinsFromCoins(rewards))
135+
s.Require().NoError(err)
136+
137+
// We'll pretend s.addrs[0] is the fee collector module.
138+
err = s.BankKeeper.SendCoinsFromAccountToModule(ctx, s.addrs[0], types.ModuleName, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(34))))
139+
s.Require().NoError(err)
140+
141+
_, err = s.app.FinalizeBlock(&abci.FinalizeBlockRequest{
142+
Height: s.app.LastBlockHeight() + 1,
143+
})
144+
s.Require().NoError(err)
145+
146+
genesisState, err := s.app.ModuleManager.ExportGenesis(ctx)
147+
s.Require().NoError(err)
148+
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
149+
s.Require().NoError(err)
150+
151+
db := coretesting.NewMemDB()
152+
conf2 := simtestutil.DefaultStartUpConfig()
153+
conf2.DB = db
154+
app2, err := simtestutil.SetupWithConfiguration(
155+
depinject.Configs(
156+
AppConfig,
157+
depinject.Supply(log.NewNopLogger()),
158+
),
159+
conf2,
160+
)
161+
s.Require().NoError(err)
162+
163+
s.clearDB(db)
164+
err = app2.CommitMultiStore().LoadLatestVersion()
165+
s.Require().NoError(err)
166+
167+
_, err = app2.InitChain(
168+
&abci.InitChainRequest{
169+
Validators: []abci.ValidatorUpdate{},
170+
ConsensusParams: simtestutil.DefaultConsensusParams,
171+
AppStateBytes: stateBytes,
172+
},
173+
)
174+
s.Require().ErrorContains(err, "distribution module balance is less than module holdings")
175+
}
176+
177+
func (s *ImportExportSuite) clearDB(db corestore.KVStoreWithBatch) {
178+
iter, err := db.Iterator(nil, nil)
179+
s.Require().NoError(err)
180+
defer iter.Close()
181+
182+
var keys [][]byte
183+
for ; iter.Valid(); iter.Next() {
184+
keys = append(keys, iter.Key())
185+
}
186+
187+
for _, k := range keys {
188+
s.Require().NoError(db.Delete(k))
189+
}
190+
}

tests/integration/gov/genesis_test.go

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ func TestImportExportQueues(t *testing.T) {
103103
assert.Assert(t, proposal1.Status == v1.StatusDepositPeriod)
104104
assert.Assert(t, proposal2.Status == v1.StatusVotingPeriod)
105105

106+
// transfer some tokens to the governance account to simulate more money being there than deposits would require
107+
err = s1.BankKeeper.SendCoinsFromAccountToModule(ctx, addrs[0], types.ModuleName, params.MinDeposit)
108+
require.NoError(t, err)
109+
106110
authGenState, err := s1.AccountKeeper.ExportGenesis(ctx)
107111
require.NoError(t, err)
108112
bankGenState, err := s1.BankKeeper.ExportGenesis(ctx)
@@ -175,7 +179,7 @@ func TestImportExportQueues(t *testing.T) {
175179
assert.Assert(t, proposal2.Status == v1.StatusVotingPeriod)
176180

177181
macc := s2.GovKeeper.GetGovernanceAccount(ctx2)
178-
assert.DeepEqual(t, sdk.Coins(params.MinDeposit), s2.BankKeeper.GetAllBalances(ctx2, macc.GetAddress()))
182+
assert.DeepEqual(t, sdk.Coins(params.MinDeposit).MulInt(sdkmath.NewInt(2)), s2.BankKeeper.GetAllBalances(ctx2, macc.GetAddress()))
179183

180184
// Run the endblocker. Check to make sure that proposal1 is removed from state, and proposal2 is finished VotingPeriod.
181185
err = s2.GovKeeper.EndBlocker(ctx2)
@@ -233,3 +237,91 @@ func TestImportExportQueues_ErrorUnconsistentState(t *testing.T) {
233237
require.NoError(t, err)
234238
require.Equal(t, genState, v1.DefaultGenesisState())
235239
}
240+
241+
func TestImportExportQueues_ErrorInsufficientBalance(t *testing.T) {
242+
var err error
243+
s1 := suite{}
244+
s1.app, err = simtestutil.SetupWithConfiguration(
245+
depinject.Configs(
246+
appConfig,
247+
depinject.Supply(log.NewNopLogger()),
248+
),
249+
simtestutil.DefaultStartUpConfig(),
250+
&s1.AccountKeeper, &s1.BankKeeper, &s1.GovKeeper, &s1.StakingKeeper, &s1.cdc, &s1.appBuilder,
251+
)
252+
assert.NilError(t, err)
253+
254+
ctx := s1.app.BaseApp.NewContext(false)
255+
addrs := simtestutil.AddTestAddrs(s1.BankKeeper, s1.StakingKeeper, ctx, 1, valTokens)
256+
257+
_, err = s1.app.FinalizeBlock(&abci.FinalizeBlockRequest{
258+
Height: s1.app.LastBlockHeight() + 1,
259+
})
260+
assert.NilError(t, err)
261+
262+
ctx = s1.app.BaseApp.NewContext(false)
263+
// Create a proposal and put it into the deposit period
264+
proposal1, err := s1.GovKeeper.SubmitProposal(ctx, []sdk.Msg{mkTestLegacyContent(t)}, "", "test", "description", addrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
265+
assert.NilError(t, err)
266+
proposalID1 := proposal1.Id
267+
268+
params, err := s1.GovKeeper.Params.Get(ctx)
269+
assert.NilError(t, err)
270+
votingStarted, err := s1.GovKeeper.AddDeposit(ctx, proposalID1, addrs[0], params.MinDeposit)
271+
assert.NilError(t, err)
272+
assert.Assert(t, votingStarted)
273+
274+
proposal1, err = s1.GovKeeper.Proposals.Get(ctx, proposalID1)
275+
assert.NilError(t, err)
276+
assert.Assert(t, proposal1.Status == v1.StatusVotingPeriod)
277+
278+
// transfer some tokens from the governance account to the user account to simulate less money being there than deposits would require
279+
err = s1.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addrs[0], sdk.Coins(params.MinDeposit).QuoInt(sdkmath.NewInt(2)))
280+
require.NoError(t, err)
281+
282+
authGenState, err := s1.AccountKeeper.ExportGenesis(ctx)
283+
require.NoError(t, err)
284+
bankGenState, err := s1.BankKeeper.ExportGenesis(ctx)
285+
require.NoError(t, err)
286+
stakingGenState, err := s1.StakingKeeper.ExportGenesis(ctx)
287+
require.NoError(t, err)
288+
289+
// export the state and import it into a new app
290+
govGenState, _ := gov.ExportGenesis(ctx, s1.GovKeeper)
291+
genesisState := s1.appBuilder.DefaultGenesis()
292+
293+
genesisState[authtypes.ModuleName] = s1.cdc.MustMarshalJSON(authGenState)
294+
genesisState[banktypes.ModuleName] = s1.cdc.MustMarshalJSON(bankGenState)
295+
genesisState[types.ModuleName] = s1.cdc.MustMarshalJSON(govGenState)
296+
genesisState[stakingtypes.ModuleName] = s1.cdc.MustMarshalJSON(stakingGenState)
297+
298+
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
299+
assert.NilError(t, err)
300+
301+
s2 := suite{}
302+
db := coretesting.NewMemDB()
303+
conf2 := simtestutil.DefaultStartUpConfig()
304+
conf2.DB = db
305+
s2.app, err = simtestutil.SetupWithConfiguration(
306+
depinject.Configs(
307+
appConfig,
308+
depinject.Supply(log.NewNopLogger()),
309+
),
310+
conf2,
311+
&s2.AccountKeeper, &s2.BankKeeper, &s2.GovKeeper, &s2.StakingKeeper, &s2.cdc, &s2.appBuilder,
312+
)
313+
assert.NilError(t, err)
314+
315+
clearDB(t, db)
316+
err = s2.app.CommitMultiStore().LoadLatestVersion()
317+
assert.NilError(t, err)
318+
319+
_, err = s2.app.InitChain(
320+
&abci.InitChainRequest{
321+
Validators: []abci.ValidatorUpdate{},
322+
ConsensusParams: simtestutil.DefaultConsensusParams,
323+
AppStateBytes: stateBytes,
324+
},
325+
)
326+
require.ErrorContains(t, err, "expected gov module to hold at least")
327+
}

x/gov/genesis.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func InitGenesis(ctx context.Context, ak types.AccountKeeper, bk types.BankKeepe
8181

8282
// check if the module account can cover the total deposits
8383
if !balance.IsAllGTE(totalDeposits) {
84-
return fmt.Errorf("expected module to hold at least %s, but it holds %s", totalDeposits, balance)
84+
return fmt.Errorf("expected gov module to hold at least %s, but it holds %s", totalDeposits, balance)
8585
}
8686
return nil
8787
}

0 commit comments

Comments
 (0)