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 #8930

Merged
merged 6 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, genState.Supply)
k.setSupply(ctx, 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 @@ -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))
}
})
}
}
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 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.
Expand Down