Skip to content

Commit f04b5dc

Browse files
atheeshpfedekunze
andauthored
fix zero coins (#9229)
* fix zero coins issue * fix tests * udpate tests * Review change Co-authored-by: Federico Kunze <[email protected]> * add change log * review changes * review changes Co-authored-by: Federico Kunze <[email protected]>
1 parent dfe3e7a commit f04b5dc

File tree

5 files changed

+79
-91
lines changed

5 files changed

+79
-91
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
134134
* (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger
135135
* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command
136136
* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value
137+
* (x/bank) [\#9229](https://github.com/cosmos/cosmos-sdk/pull/9229) Now zero coin balances cannot be added to balances & supply stores. If any denom becomes zero corresponding key gets deleted from store.
137138

138139
### Deprecated
139140

x/bank/keeper/genesis_test.go

-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ func (suite *IntegrationTestSuite) TestExportGenesis() {
3333
suite.Require().Len(exportGenesis.Params.SendEnabled, 0)
3434
suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled)
3535
suite.Require().Equal(totalSupply, exportGenesis.Supply)
36-
// add mint module balance as nil
37-
expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil})
3836
suite.Require().Equal(expectedBalances, exportGenesis.Balances)
3937
suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata)
4038
}

x/bank/keeper/keeper.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ func (k BaseKeeper) GetPaginatedTotalSupply(ctx sdk.Context, pagination *query.P
6767
if err != nil {
6868
return fmt.Errorf("unable to convert amount string to Int %v", err)
6969
}
70-
supply = append(supply, sdk.NewCoin(string(key), amount))
70+
71+
// `Add` omits the 0 coins addition to the `supply`.
72+
supply = supply.Add(sdk.NewCoin(string(key), amount))
7173
return nil
7274
})
7375

@@ -124,7 +126,7 @@ func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr
124126
balances := sdk.NewCoins()
125127

126128
for _, coin := range amt {
127-
balance := k.GetBalance(ctx, delegatorAddr, coin.Denom)
129+
balance := k.GetBalance(ctx, delegatorAddr, coin.GetDenom())
128130
if balance.IsLT(coin) {
129131
return sdkerrors.Wrapf(
130132
sdkerrors.ErrInsufficientFunds, "failed to delegate; %s is smaller than %s", balance, amt,
@@ -426,14 +428,20 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amounts sdk.Co
426428
}
427429

428430
func (k BaseKeeper) setSupply(ctx sdk.Context, coin sdk.Coin) {
429-
store := ctx.KVStore(k.storeKey)
430-
supplyStore := prefix.NewStore(store, types.SupplyKey)
431-
432431
intBytes, err := coin.Amount.Marshal()
433432
if err != nil {
434433
panic(fmt.Errorf("unable to marshal amount value %v", err))
435434
}
436-
supplyStore.Set([]byte(coin.GetDenom()), intBytes)
435+
436+
store := ctx.KVStore(k.storeKey)
437+
supplyStore := prefix.NewStore(store, types.SupplyKey)
438+
439+
// Bank invariants and IBC requires to remove zero coins.
440+
if coin.IsZero() {
441+
supplyStore.Delete([]byte(coin.GetDenom()))
442+
} else {
443+
supplyStore.Set([]byte(coin.GetDenom()), intBytes)
444+
}
437445
}
438446

439447
func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balance, amt sdk.Coins) error {

x/bank/keeper/keeper_test.go

+57-81
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,28 @@ type IntegrationTestSuite struct {
7272
queryClient types.QueryClient
7373
}
7474

75+
func (suite *IntegrationTestSuite) initKeepersWithmAccPerms(blockedAddrs map[string]bool) (authkeeper.AccountKeeper, keeper.BaseKeeper) {
76+
app := suite.app
77+
maccPerms := simapp.GetMaccPerms()
78+
appCodec := simapp.MakeTestEncodingConfig().Marshaler
79+
80+
maccPerms[holder] = nil
81+
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
82+
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
83+
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
84+
maccPerms[randomPerm] = []string{"random"}
85+
authKeeper := authkeeper.NewAccountKeeper(
86+
appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName),
87+
authtypes.ProtoBaseAccount, maccPerms,
88+
)
89+
keeper := keeper.NewBaseKeeper(
90+
appCodec, app.GetKey(types.StoreKey), authKeeper,
91+
app.GetSubspace(types.ModuleName), blockedAddrs,
92+
)
93+
94+
return authKeeper, keeper
95+
}
96+
7597
func (suite *IntegrationTestSuite) SetupTest() {
7698
app := simapp.Setup(false)
7799
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
@@ -89,42 +111,41 @@ func (suite *IntegrationTestSuite) SetupTest() {
89111
}
90112

91113
func (suite *IntegrationTestSuite) TestSupply() {
92-
app, ctx := suite.app, suite.ctx
114+
_, ctx := suite.app, suite.ctx
115+
116+
require := suite.Require()
117+
118+
// add module accounts to supply keeper
119+
authKeeper, keeper := suite.initKeepersWithmAccPerms(make(map[string]bool))
93120

94121
initialPower := int64(100)
95122
initTokens := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, initialPower)
96-
97123
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
98-
suite.NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, totalSupply))
99124

100-
total, _, err := app.BankKeeper.GetPaginatedTotalSupply(ctx, &query.PageRequest{})
101-
suite.Require().NoError(err)
102-
suite.Require().Equal(totalSupply, total)
125+
// set burnerAcc balance
126+
authKeeper.SetModuleAccount(ctx, burnerAcc)
127+
require.NoError(keeper.MintCoins(ctx, authtypes.Minter, totalSupply))
128+
require.NoError(keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Minter, burnerAcc.GetAddress(), totalSupply))
129+
130+
total, _, err := keeper.GetPaginatedTotalSupply(ctx, &query.PageRequest{})
131+
require.NoError(err)
132+
require.Equal(totalSupply, total)
133+
134+
// burning all supplied tokens
135+
err = keeper.BurnCoins(ctx, authtypes.Burner, totalSupply)
136+
require.NoError(err)
137+
138+
total, _, err = keeper.GetPaginatedTotalSupply(ctx, &query.PageRequest{})
139+
require.NoError(err)
140+
require.Equal(total.String(), "")
103141
}
104142

105143
func (suite *IntegrationTestSuite) TestSendCoinsFromModuleToAccount_Blacklist() {
106-
app := simapp.Setup(false)
107-
ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1})
108-
appCodec := app.AppCodec()
144+
ctx := suite.ctx
109145

110146
// add module accounts to supply keeper
111-
maccPerms := simapp.GetMaccPerms()
112-
maccPerms[holder] = nil
113-
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
114-
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
115-
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
116-
maccPerms[randomPerm] = []string{"random"}
117-
118147
addr1 := sdk.AccAddress([]byte("addr1_______________"))
119-
120-
authKeeper := authkeeper.NewAccountKeeper(
121-
appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName),
122-
authtypes.ProtoBaseAccount, maccPerms,
123-
)
124-
keeper := keeper.NewBaseKeeper(
125-
appCodec, app.GetKey(types.StoreKey), authKeeper,
126-
app.GetSubspace(types.ModuleName), map[string]bool{addr1.String(): true},
127-
)
148+
_, keeper := suite.initKeepersWithmAccPerms(map[string]bool{addr1.String(): true})
128149

129150
suite.Require().NoError(keeper.MintCoins(ctx, minttypes.ModuleName, initCoins))
130151
suite.Require().Error(keeper.SendCoinsFromModuleToAccount(
@@ -133,26 +154,10 @@ func (suite *IntegrationTestSuite) TestSendCoinsFromModuleToAccount_Blacklist()
133154
}
134155

135156
func (suite *IntegrationTestSuite) TestSupply_SendCoins() {
136-
app := simapp.Setup(false)
137-
ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1})
138-
appCodec := app.AppCodec()
157+
ctx := suite.ctx
139158

140159
// add module accounts to supply keeper
141-
maccPerms := simapp.GetMaccPerms()
142-
maccPerms[holder] = nil
143-
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
144-
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
145-
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
146-
maccPerms[randomPerm] = []string{"random"}
147-
148-
authKeeper := authkeeper.NewAccountKeeper(
149-
appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName),
150-
authtypes.ProtoBaseAccount, maccPerms,
151-
)
152-
keeper := keeper.NewBaseKeeper(
153-
appCodec, app.GetKey(types.StoreKey), authKeeper,
154-
app.GetSubspace(types.ModuleName), make(map[string]bool),
155-
)
160+
authKeeper, keeper := suite.initKeepersWithmAccPerms(make(map[string]bool))
156161

157162
baseAcc := authKeeper.NewAccountWithAddress(ctx, authtypes.NewModuleAddress("baseAcc"))
158163

@@ -203,26 +208,10 @@ func (suite *IntegrationTestSuite) TestSupply_SendCoins() {
203208
}
204209

205210
func (suite *IntegrationTestSuite) TestSupply_MintCoins() {
206-
app := simapp.Setup(false)
207-
ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1})
208-
appCodec := app.AppCodec()
211+
ctx := suite.ctx
209212

210213
// add module accounts to supply keeper
211-
maccPerms := simapp.GetMaccPerms()
212-
maccPerms[holder] = nil
213-
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
214-
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
215-
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
216-
maccPerms[randomPerm] = []string{"random"}
217-
218-
authKeeper := authkeeper.NewAccountKeeper(
219-
appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName),
220-
authtypes.ProtoBaseAccount, maccPerms,
221-
)
222-
keeper := keeper.NewBaseKeeper(
223-
appCodec, app.GetKey(types.StoreKey), authKeeper,
224-
app.GetSubspace(types.ModuleName), make(map[string]bool),
225-
)
214+
authKeeper, keeper := suite.initKeepersWithmAccPerms(make(map[string]bool))
226215

227216
authKeeper.SetModuleAccount(ctx, burnerAcc)
228217
authKeeper.SetModuleAccount(ctx, minterAcc)
@@ -264,26 +253,9 @@ func (suite *IntegrationTestSuite) TestSupply_MintCoins() {
264253
}
265254

266255
func (suite *IntegrationTestSuite) TestSupply_BurnCoins() {
267-
app := simapp.Setup(false)
268-
ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1})
269-
appCodec := simapp.MakeTestEncodingConfig().Marshaler
270-
256+
ctx := suite.ctx
271257
// add module accounts to supply keeper
272-
maccPerms := simapp.GetMaccPerms()
273-
maccPerms[holder] = nil
274-
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
275-
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
276-
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
277-
maccPerms[randomPerm] = []string{"random"}
278-
279-
authKeeper := authkeeper.NewAccountKeeper(
280-
appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName),
281-
authtypes.ProtoBaseAccount, maccPerms,
282-
)
283-
keeper := keeper.NewBaseKeeper(
284-
appCodec, app.GetKey(types.StoreKey), authKeeper,
285-
app.GetSubspace(types.ModuleName), make(map[string]bool),
286-
)
258+
authKeeper, keeper := suite.initKeepersWithmAccPerms(make(map[string]bool))
287259

288260
// set burnerAcc balance
289261
authKeeper.SetModuleAccount(ctx, burnerAcc)
@@ -349,11 +321,15 @@ func (suite *IntegrationTestSuite) TestSendCoinsNewAccount() {
349321
app.BankKeeper.GetAllBalances(ctx, addr2)
350322
suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addr2))
351323

352-
sendAmt := sdk.NewCoins(newFooCoin(50), newBarCoin(25))
324+
sendAmt := sdk.NewCoins(newFooCoin(50), newBarCoin(50))
353325
suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt))
354326

355327
acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2)
328+
acc1Balances = app.BankKeeper.GetAllBalances(ctx, addr1)
356329
suite.Require().Equal(sendAmt, acc2Balances)
330+
updatedAcc1Bal := balances.Sub(sendAmt)
331+
suite.Require().Len(acc1Balances, len(updatedAcc1Bal))
332+
suite.Require().Equal(acc1Balances, updatedAcc1Bal)
357333
suite.Require().NotNil(app.AccountKeeper.GetAccount(ctx, addr2))
358334
}
359335

x/bank/keeper/send.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,13 @@ func (k BaseSendKeeper) setBalance(ctx sdk.Context, addr sdk.AccAddress, balance
266266

267267
accountStore := k.getAccountStore(ctx, addr)
268268

269-
bz := k.cdc.MustMarshal(&balance)
270-
accountStore.Set([]byte(balance.Denom), bz)
269+
// Bank invariants require to not store zero balances.
270+
if balance.IsZero() {
271+
accountStore.Delete([]byte(balance.Denom))
272+
} else {
273+
bz := k.cdc.MustMarshal(&balance)
274+
accountStore.Set([]byte(balance.Denom), bz)
275+
}
271276

272277
return nil
273278
}

0 commit comments

Comments
 (0)