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
1 change: 1 addition & 0 deletions simapp/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func AppStateRandomizedFn(
GenState: genesisState,
Accounts: accs,
InitialStake: initialStake,
// NumBonded: 0,
NumBonded: numInitiallyBonded,
GenTimestamp: genesisTimestamp,
}
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.GetSupply(suite.ctx).GetTotal())
}
})
}
}
16 changes: 11 additions & 5 deletions x/bank/simulation/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
// }
})
}
}
35 changes: 35 additions & 0 deletions x/bank/simulation/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
}
Expand Down
26 changes: 16 additions & 10 deletions x/bank/simulation/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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),
Expand All @@ -41,15 +44,17 @@ 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())
}

// 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)
Expand All @@ -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"},
}

Expand Down
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