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/simapp/state.go b/simapp/state.go index 4c3773813a04..13195cd161de 100644 --- a/simapp/state.go +++ b/simapp/state.go @@ -113,6 +113,7 @@ func AppStateRandomizedFn( GenState: genesisState, Accounts: accs, InitialStake: initialStake, + // NumBonded: 0, NumBonded: numInitiallyBonded, GenTimestamp: genesisTimestamp, } diff --git a/x/bank/keeper/genesis.go b/x/bank/keeper/genesis.go index d30415c6a285..ca974a74c7c7 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, types.NewSupply(genState.Supply)) + k.SetSupply(ctx, &types.Supply{Total: 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 56c95c04bee9..88f61dd1a7b7 100644 --- a/x/bank/keeper/genesis_test.go +++ b/x/bank/keeper/genesis_test.go @@ -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.GetSupply(suite.ctx).GetTotal()) + } + }) + } +} diff --git a/x/bank/simulation/decoder_test.go b/x/bank/simulation/decoder_test.go index 82ab32c1497b..846826f781be 100644 --- a/x/bank/simulation/decoder_test.go +++ b/x/bank/simulation/decoder_test.go @@ -32,20 +32,26 @@ func TestDecodeStore(t *testing.T) { tests := []struct { name string expectedLog string + expectedErr bool }{ - {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)}, - {"other", ""}, + {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply), false}, + {"other", "", true}, } for i, tt := range tests { i, tt := i, tt t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: + if tt.expectedErr { require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) - default: + } else { require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } + // switch i { + // case len(tests) - 1: + // require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) + // default: + // require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) + // } }) } } diff --git a/x/bank/simulation/genesis.go b/x/bank/simulation/genesis.go index 9031d0336451..4737eaa7f63e 100644 --- a/x/bank/simulation/genesis.go +++ b/x/bank/simulation/genesis.go @@ -50,6 +50,20 @@ func RandomGenesisBalances(simState *module.SimulationState) []types.Balance { return genesisBalances } +// func randomBondedAccountsGenesisBalances(randomGenesisBalance []types.Balance, numBonded int64, initialStake int64) []types.Balance { +// genesisBalances := randomGenesisBalance +// s := rand.NewSource(1) +// r := rand.New(s) +// accounts := simtypes.RandomAccounts(r, int(numBonded)) +// for _, acc := range accounts { +// genesisBalances = append(genesisBalances, types.Balance{ +// Address: acc.Address.String(), +// Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(initialStake))), +// }) +// } +// return genesisBalances +// } + // RandomizedGenState generates a random GenesisState for bank func RandomizedGenState(simState *module.SimulationState) { var sendEnabledParams types.SendEnabledParams @@ -68,11 +82,32 @@ func RandomizedGenState(simState *module.SimulationState) { totalSupply := sdk.NewInt(simState.InitialStake * (numAccs + simState.NumBonded)) supply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply)) + // genesisBalances := []types.Balance{} + // s := rand.NewSource(1) + // r := rand.New(s) + // accounts := simtypes.RandomAccounts(r, int(simState.NumBonded)) + // fmt.Println("===================") + // fmt.Println(len(accounts)) + // fmt.Println(len(simState.Accounts)) + // for _, acc := range accounts { + // genesisBalances = append(genesisBalances, types.Balance{ + // Address: acc.Address.String(), + // Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))), + // }) + // } + // simStateBalances := RandomGenesisBalances(simState) + // for i := 0; i < len(simStateBalances); i++ { + // genesisBalances = append(genesisBalances, simStateBalances...) + // } + + // genesisBalance := RandomGenesisBalances(simState) + // totalBalance := randomBondedAccountsGenesisBalances(genesisBalance, simState.NumBonded, simState.InitialStake) bankGenesis := types.GenesisState{ Params: types.Params{ SendEnabled: sendEnabledParams, DefaultSendEnabled: defaultSendEnabledParam, }, + // Balances: totalBalance, Balances: RandomGenesisBalances(simState), Supply: supply, } diff --git a/x/bank/simulation/genesis_test.go b/x/bank/simulation/genesis_test.go index fc31ca38e9ef..daa8d14bb762 100644 --- a/x/bank/simulation/genesis_test.go +++ b/x/bank/simulation/genesis_test.go @@ -7,8 +7,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" + // "github.com/cosmos/cosmos-sdk/codec" + // codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/bank/simulation" @@ -18,14 +19,16 @@ import ( // TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. // Abonormal scenarios are not tested here. func TestRandomizedGenState(t *testing.T) { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(interfaceRegistry) + // interfaceRegistry := codectypes.NewInterfaceRegistry() + // cdc := codec.NewProtoCodec(interfaceRegistry) + app := simapp.Setup(false) s := rand.NewSource(1) r := rand.New(s) simState := module.SimulationState{ - AppParams: make(simtypes.AppParams), - Cdc: cdc, + AppParams: make(simtypes.AppParams), + // Cdc: cdc, + Cdc: app.AppCodec(), Rand: r, NumBonded: 3, Accounts: simtypes.RandomAccounts(r, 3), @@ -41,6 +44,7 @@ func TestRandomizedGenState(t *testing.T) { require.Equal(t, true, bankGenesis.Params.GetDefaultSendEnabled()) require.Len(t, bankGenesis.Params.GetSendEnabled(), 1) require.Len(t, bankGenesis.Balances, 3) + // require.Len(t, bankGenesis.Balances, 6) require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", bankGenesis.Balances[2].GetAddress().String()) require.Equal(t, "1000stake", bankGenesis.Balances[2].GetCoins().String()) require.Equal(t, "6000stake", bankGenesis.Supply.String()) @@ -48,8 +52,9 @@ func TestRandomizedGenState(t *testing.T) { // TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. func TestRandomizedGenState1(t *testing.T) { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(interfaceRegistry) + // interfaceRegistry := codectypes.NewInterfaceRegistry() + // cdc := codec.NewProtoCodec(interfaceRegistry) + app := simapp.Setup(false) s := rand.NewSource(1) r := rand.New(s) @@ -64,8 +69,9 @@ func TestRandomizedGenState1(t *testing.T) { { // panic => reason: incomplete initialization of the simState module.SimulationState{ AppParams: make(simtypes.AppParams), - Cdc: cdc, - Rand: r, + // Cdc: cdc, + Cdc: app.AppCodec(), + Rand: r, }, "assignment to entry in nil map"}, } diff --git a/x/bank/types/genesis.go b/x/bank/types/genesis.go index 4adc758f3bd9..e7d4b07ae9bb 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 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.