From e9e978d543a3ad7ccb1eaf42883569398dc54ae6 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Mon, 22 Mar 2021 14:15:29 -0400 Subject: [PATCH] Fix genesis supply handling (#8930) * Fix genesis supply handling * Add test * Fix nit Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com> Co-authored-by: Alessio Treglia Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- proto/cosmos/bank/v1beta1/genesis.proto | 3 +- x/bank/keeper/genesis.go | 8 ++--- x/bank/keeper/genesis_test.go | 47 +++++++++++++++++++++++++ x/bank/types/genesis.go | 19 ++++++++-- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/proto/cosmos/bank/v1beta1/genesis.proto b/proto/cosmos/bank/v1beta1/genesis.proto index 25c80a38b5ab..8fd7329a0a5d 100644 --- a/proto/cosmos/bank/v1beta1/genesis.proto +++ b/proto/cosmos/bank/v1beta1/genesis.proto @@ -15,7 +15,8 @@ message GenesisState { // balances is an array containing the balances of all the accounts. repeated Balance balances = 2 [(gogoproto.nullable) = false]; - // supply represents the total supply. + // supply represents the total supply. If it is left empty, then supply will be calculated based on the provided + // balances. Otherwise, it will be used to validate that the sum of the balances equals this amount. repeated cosmos.base.v1beta1.Coin supply = 3 [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; diff --git a/x/bank/keeper/genesis.go b/x/bank/keeper/genesis.go index dc73ae0b0632..f0c680b24a74 100644 --- a/x/bank/keeper/genesis.go +++ b/x/bank/keeper/genesis.go @@ -11,7 +11,7 @@ import ( func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { k.SetParams(ctx, genState.Params) - var totalSupply sdk.Coins + totalSupply := sdk.Coins{} genState.Balances = types.SanitizeGenesisBalances(genState.Balances) for _, balance := range genState.Balances { @@ -27,11 +27,11 @@ func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { totalSupply = totalSupply.Add(balance.Coins...) } - if genState.Supply.Empty() { - genState.Supply = totalSupply + if !genState.Supply.Empty() && !genState.Supply.IsEqual(totalSupply) { + panic(fmt.Errorf("genesis supply is incorrect, expected %v, got %v", genState.Supply, totalSupply)) } - k.setSupply(ctx, genState.Supply) + k.setSupply(ctx, totalSupply) for _, meta := range genState.DenomMetadata { k.SetDenomMetaData(ctx, meta) diff --git a/x/bank/keeper/genesis_test.go b/x/bank/keeper/genesis_test.go index 6b148a006c02..494907ca309e 100644 --- a/x/bank/keeper/genesis_test.go +++ b/x/bank/keeper/genesis_test.go @@ -64,3 +64,50 @@ func (suite *IntegrationTestSuite) TestInitGenesis() { suite.Require().True(found) suite.Require().Equal(m, m2) } + +func (suite *IntegrationTestSuite) TestTotalSupply() { + // Prepare some test data. + defaultGenesis := types.DefaultGenesisState() + balances := []types.Balance{ + {Coins: sdk.NewCoins(sdk.NewCoin("foocoin", sdk.NewInt(1))), Address: "cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0"}, + {Coins: sdk.NewCoins(sdk.NewCoin("barcoin", sdk.NewInt(1))), Address: "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"}, + {Coins: sdk.NewCoins(sdk.NewCoin("foocoin", sdk.NewInt(10)), sdk.NewCoin("barcoin", sdk.NewInt(20))), Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q"}, + } + totalSupply := sdk.NewCoins(sdk.NewCoin("foocoin", sdk.NewInt(11)), sdk.NewCoin("barcoin", sdk.NewInt(21))) + + testcases := []struct { + name string + genesis *types.GenesisState + expSupply sdk.Coins + expPanic bool + expPanicMsg string + }{ + { + "calculation NOT matching genesis Supply field", + types.NewGenesisState(defaultGenesis.Params, balances, sdk.NewCoins(sdk.NewCoin("wrongcoin", sdk.NewInt(1))), defaultGenesis.DenomMetadata), + nil, true, "genesis supply is incorrect, expected 1wrongcoin, got 21barcoin,11foocoin", + }, + { + "calculation matches genesis Supply field", + types.NewGenesisState(defaultGenesis.Params, balances, totalSupply, defaultGenesis.DenomMetadata), + totalSupply, false, "", + }, + { + "calculation is correct, empty genesis Supply field", + types.NewGenesisState(defaultGenesis.Params, balances, nil, defaultGenesis.DenomMetadata), + totalSupply, false, "", + }, + } + + for _, tc := range testcases { + tc := tc + suite.Run(tc.name, func() { + if tc.expPanic { + suite.PanicsWithError(tc.expPanicMsg, func() { suite.app.BankKeeper.InitGenesis(suite.ctx, tc.genesis) }) + } else { + suite.app.BankKeeper.InitGenesis(suite.ctx, tc.genesis) + suite.Require().Equal(tc.expSupply, suite.app.BankKeeper.GetTotalSupply(suite.ctx)) + } + }) + } +} diff --git a/x/bank/types/genesis.go b/x/bank/types/genesis.go index 03f4be4abd63..d40093126f2b 100644 --- a/x/bank/types/genesis.go +++ b/x/bank/types/genesis.go @@ -18,6 +18,8 @@ func (gs GenesisState) Validate() error { seenBalances := make(map[string]bool) seenMetadatas := make(map[string]bool) + totalSupply := sdk.Coins{} + for _, balance := range gs.Balances { if seenBalances[balance.Address] { return fmt.Errorf("duplicate balance for address %s", balance.Address) @@ -28,6 +30,8 @@ func (gs GenesisState) Validate() error { } seenBalances[balance.Address] = true + + totalSupply = totalSupply.Add(balance.Coins...) } for _, metadata := range gs.DenomMetadata { @@ -42,8 +46,19 @@ func (gs GenesisState) Validate() error { seenMetadatas[metadata.Base] = true } - // NOTE: this errors if supply for any given coin is zero - return gs.Supply.Validate() + if !gs.Supply.Empty() { + // NOTE: this errors if supply for any given coin is zero + err := gs.Supply.Validate() + if err != nil { + return err + } + + if !gs.Supply.IsEqual(totalSupply) { + return fmt.Errorf("genesis supply is incorrect, expected %v, got %v", gs.Supply, totalSupply) + } + } + + return nil } // NewGenesisState creates a new genesis state.