Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix genesis supply handling (backport #8930) #9252

Closed
wants to merge 11 commits into from
3 changes: 2 additions & 1 deletion proto/cosmos/bank/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down
8 changes: 4 additions & 4 deletions x/bank/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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, types.NewSupply(genState.Supply))
k.SetSupply(ctx, &types.Supply{Total: totalSupply})

for _, meta := range genState.DenomMetadata {
k.SetDenomMetaData(ctx, meta)
Expand Down
47 changes: 47 additions & 0 deletions x/bank/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,50 @@ func (suite *IntegrationTestSuite) TestInitGenesis() {
m2 := bk.GetDenomMetaData(suite.ctx, m.Base)
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))
}
})
}
}
19 changes: 17 additions & 2 deletions x/bank/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -28,6 +30,8 @@ func (gs GenesisState) Validate() error {
}

seenBalances[balance.Address] = true

totalSupply = totalSupply.Add(balance.Coins...)
}

for _, metadata := range gs.DenomMetadata {
Expand All @@ -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 NewSupply(gs.Supply).ValidateBasic()
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.
Expand Down