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 <alessio@tendermint.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Aaron Craelius 2021-03-22 14:15:29 -04:00 committed by GitHub
parent 3361ea989c
commit e9e978d543
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 7 deletions

View File

@ -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];

View File

@ -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)

View File

@ -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))
}
})
}
}

View File

@ -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.