diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index 447f039f751d9..66f5326489a47 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -24,3 +24,4 @@ $mockgen_cmd -source=x/evidence/types/expected_keepers.go -package testutil -des $mockgen_cmd -source=x/slashing/types/expected_keepers.go -package testutil -destination x/slashing/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/genutil/types/expected_keepers.go -package testutil -destination x/genutil/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/gov/testutil/expected_keepers.go -package testutil -destination x/gov/testutil/expected_keepers_mocks.go +$mockgen_cmd -source=x/staking/types/expected_keepers.go -package testutil -destination x/staking/testutil/expected_keepers_mocks.go diff --git a/tests/e2e/staking/client/testutil/cli_test.go b/tests/e2e/staking/client/testutil/cli_test.go index 3bfc2f15edba9..3f51a2bc701da 100644 --- a/tests/e2e/staking/client/testutil/cli_test.go +++ b/tests/e2e/staking/client/testutil/cli_test.go @@ -8,13 +8,11 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/network" - "github.com/cosmos/cosmos-sdk/x/staking/client/testutil" - "github.com/stretchr/testify/suite" ) func TestIntegrationTestSuite(t *testing.T) { cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) cfg.NumValidators = 2 - suite.Run(t, testutil.NewIntegrationTestSuite(cfg)) + suite.Run(t, NewIntegrationTestSuite(cfg)) } diff --git a/x/staking/client/testutil/grpc.go b/tests/e2e/staking/client/testutil/grpc.go similarity index 100% rename from x/staking/client/testutil/grpc.go rename to tests/e2e/staking/client/testutil/grpc.go diff --git a/x/staking/client/testutil/suite.go b/tests/e2e/staking/client/testutil/suite.go similarity index 100% rename from x/staking/client/testutil/suite.go rename to tests/e2e/staking/client/testutil/suite.go diff --git a/x/staking/client/testutil/test_helpers.go b/tests/e2e/staking/client/testutil/test_helpers.go similarity index 100% rename from x/staking/client/testutil/test_helpers.go rename to tests/e2e/staking/client/testutil/test_helpers.go diff --git a/x/staking/keeper/common_test.go b/tests/integration/staking/keeper/common_test.go similarity index 100% rename from x/staking/keeper/common_test.go rename to tests/integration/staking/keeper/common_test.go diff --git a/tests/integration/staking/keeper/delegation_test.go b/tests/integration/staking/keeper/delegation_test.go new file mode 100644 index 0000000000000..1a34c9e261c61 --- /dev/null +++ b/tests/integration/staking/keeper/delegation_test.go @@ -0,0 +1,98 @@ +package keeper_test + +import ( + "testing" + "time" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestUnbondingDelegationsMaxEntries(t *testing.T) { + _, app, ctx := createTestInput(t) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) + addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + + startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + + bondDenom := app.StakingKeeper.BondDenom(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens)))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator and a delegator to that validator + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + + validator, issuedShares := validator.AddTokensFromDel(startTokens) + require.Equal(t, startTokens, issuedShares.RoundInt()) + + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.True(math.IntEq(t, startTokens, validator.BondedTokens())) + require.True(t, validator.IsBonded()) + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) + + maxEntries := app.StakingKeeper.MaxEntries(ctx) + + oldBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + + // should all pass + var completionTime time.Time + for i := int64(0); i < int64(maxEntries); i++ { + var err error + ctx = ctx.WithBlockHeight(i) + completionTime, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) + require.NoError(t, err) + } + + newBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(int64(maxEntries)))) + require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(int64(maxEntries)))) + + oldBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + + // an additional unbond should fail due to max entries + _, err := app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) + require.Error(t, err) + + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + + require.True(math.IntEq(t, newBonded, oldBonded)) + require.True(math.IntEq(t, newNotBonded, oldNotBonded)) + + // mature unbonding delegations + ctx = ctx.WithBlockTime(completionTime) + _, err = app.StakingKeeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) + require.NoError(t, err) + + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, newBonded, oldBonded)) + require.True(math.IntEq(t, newNotBonded, oldNotBonded.SubRaw(int64(maxEntries)))) + + oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + + // unbonding should work again + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) + require.NoError(t, err) + + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(1))) + require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(1))) +} diff --git a/x/staking/keeper/genesis_test.go b/tests/integration/staking/keeper/genesis_test.go similarity index 100% rename from x/staking/keeper/genesis_test.go rename to tests/integration/staking/keeper/genesis_test.go diff --git a/tests/integration/staking/keeper/grpc_query_test.go b/tests/integration/staking/keeper/grpc_query_test.go new file mode 100644 index 0000000000000..055fcf26e38cb --- /dev/null +++ b/tests/integration/staking/keeper/grpc_query_test.go @@ -0,0 +1,740 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (suite *IntegrationTestSuite) TestGRPCQueryValidators() { + queryClient, vals := suite.queryClient, suite.vals + var req *types.QueryValidatorsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + numVals int + hasNext bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorsRequest{} + }, + true, + + len(vals) + 1, // +1 validator from genesis state + false, + }, + { + "empty status returns all the validators", + func() { + req = &types.QueryValidatorsRequest{Status: ""} + }, + true, + len(vals) + 1, // +1 validator from genesis state + false, + }, + { + "invalid request", + func() { + req = &types.QueryValidatorsRequest{Status: "test"} + }, + false, + 0, + false, + }, + { + "valid request", + func() { + req = &types.QueryValidatorsRequest{ + Status: types.Bonded.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + 1, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + valsResp, err := queryClient.Validators(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.NotNil(valsResp) + suite.Equal(tc.numVals, len(valsResp.Validators)) + suite.Equal(uint64(len(vals))+1, valsResp.Pagination.Total) // +1 validator from genesis state + + if tc.hasNext { + suite.NotNil(valsResp.Pagination.NextKey) + } else { + suite.Nil(valsResp.Pagination.NextKey) + } + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorValidators() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + params := app.StakingKeeper.GetParams(ctx) + delValidators := app.StakingKeeper.GetDelegatorValidators(ctx, addrs[0], params.MaxValidators) + var req *types.QueryDelegatorValidatorsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorValidatorsRequest{} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegatorValidatorsRequest{ + DelegatorAddr: addrs[0].String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorValidators(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(1, len(res.Validators)) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(len(delValidators)), res.Pagination.Total) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorValidator() { + queryClient, addrs, vals := suite.queryClient, suite.addrs, suite.vals + addr := addrs[1] + addrVal, addrVal1 := vals[0].OperatorAddress, vals[1].OperatorAddress + var req *types.QueryDelegatorValidatorRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorValidatorRequest{} + }, + false, + }, + { + "invalid delegator, validator pair", + func() { + req = &types.QueryDelegatorValidatorRequest{ + DelegatorAddr: addr.String(), + ValidatorAddr: addrVal, + } + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegatorValidatorRequest{ + DelegatorAddr: addr.String(), + ValidatorAddr: addrVal1, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorValidator(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(addrVal1, res.Validator.OperatorAddress) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryDelegation() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc, addrAcc1 := addrs[0], addrs[1] + addrVal := vals[0].OperatorAddress + valAddr, err := sdk.ValAddressFromBech32(addrVal) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + var req *types.QueryDelegationRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegationRequest{} + }, + false, + }, + { + "invalid validator, delegator pair", + func() { + req = &types.QueryDelegationRequest{ + DelegatorAddr: addrAcc1.String(), + ValidatorAddr: addrVal, + } + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegationRequest{DelegatorAddr: addrAcc.String(), ValidatorAddr: addrVal} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.Delegation(gocontext.Background(), req) + if tc.expPass { + suite.Equal(delegation.ValidatorAddress, res.DelegationResponse.Delegation.ValidatorAddress) + suite.Equal(delegation.DelegatorAddress, res.DelegationResponse.Delegation.DelegatorAddress) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponse.Balance) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc := addrs[0] + addrVal1 := vals[0].OperatorAddress + valAddr, err := sdk.ValAddressFromBech32(addrVal1) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + var req *types.QueryDelegatorDelegationsRequest + + testCases := []struct { + msg string + malleate func() + onSuccess func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) + expErr bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorDelegationsRequest{} + }, + func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) {}, + true, + }, + { + "valid request with no delegations", + func() { + req = &types.QueryDelegatorDelegationsRequest{DelegatorAddr: addrs[4].String()} + }, + func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) { + suite.Equal(uint64(0), response.Pagination.Total) + suite.Len(response.DelegationResponses, 0) + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegatorDelegationsRequest{ + DelegatorAddr: addrAcc.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) { + suite.Equal(uint64(2), response.Pagination.Total) + suite.Len(response.DelegationResponses, 1) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), response.DelegationResponses[0].Balance) + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorDelegations(gocontext.Background(), req) + if tc.expErr { + suite.Error(err) + } else { + suite.NoError(err) + tc.onSuccess(suite, res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryValidatorDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc := addrs[0] + addrVal1 := vals[1].OperatorAddress + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + addrVal2 := valAddrs[4] + valAddr, err := sdk.ValAddressFromBech32(addrVal1) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + + var req *types.QueryValidatorDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorDelegationsRequest{} + }, + false, + true, + }, + { + "invalid validator delegator pair", + func() { + req = &types.QueryValidatorDelegationsRequest{ValidatorAddr: addrVal2.String()} + }, + false, + false, + }, + { + "valid request", + func() { + req = &types.QueryValidatorDelegationsRequest{ + ValidatorAddr: addrVal1, + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.ValidatorDelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Len(res.DelegationResponses, 1) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(2), res.Pagination.Total) + suite.Equal(addrVal1, res.DelegationResponses[0].Delegation.ValidatorAddress) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponses[0].Balance) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.DelegationResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryUnbondingDelegation() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc2 := addrs[1] + addrVal2 := vals[1].OperatorAddress + + unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) + valAddr, err1 := sdk.ValAddressFromBech32(addrVal2) + suite.NoError(err1) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc2, valAddr, sdk.NewDecFromInt(unbondingTokens)) + suite.NoError(err) + + unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc2, valAddr) + suite.True(found) + var req *types.QueryUnbondingDelegationRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryUnbondingDelegationRequest{} + }, + false, + }, + { + "invalid request", + func() { + req = &types.QueryUnbondingDelegationRequest{} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryUnbondingDelegationRequest{ + DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal2, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.UnbondingDelegation(gocontext.Background(), req) + if tc.expPass { + suite.NotNil(res) + suite.Equal(unbond, res.Unbond) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorUnbondingDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc, addrAcc1 := addrs[0], addrs[1] + addrVal, addrVal2 := vals[0].OperatorAddress, vals[1].OperatorAddress + + unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) + valAddr1, err1 := sdk.ValAddressFromBech32(addrVal) + suite.NoError(err1) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr1, sdk.NewDecFromInt(unbondingTokens)) + suite.NoError(err) + valAddr2, err1 := sdk.ValAddressFromBech32(addrVal2) + suite.NoError(err1) + _, err = app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr2, sdk.NewDecFromInt(unbondingTokens)) + suite.NoError(err) + + unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc, valAddr1) + suite.True(found) + var req *types.QueryDelegatorUnbondingDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{} + }, + false, + true, + }, + { + "invalid request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{DelegatorAddr: addrAcc1.String()} + }, + false, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: addrAcc.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorUnbondingDelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(2), res.Pagination.Total) + suite.Len(res.UnbondingResponses, 1) + suite.Equal(unbond, res.UnbondingResponses[0]) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.UnbondingResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryPoolParameters() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + bondDenom := sdk.DefaultBondDenom + + // Query pool + res, err := queryClient.Pool(gocontext.Background(), &types.QueryPoolRequest{}) + suite.NoError(err) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + suite.Equal(app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, res.Pool.NotBondedTokens) + suite.Equal(app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, res.Pool.BondedTokens) + + // Query Params + resp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) + suite.NoError(err) + suite.Equal(app.StakingKeeper.GetParams(ctx), resp.Params) +} + +func (suite *IntegrationTestSuite) TestGRPCQueryHistoricalInfo() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + hi, found := app.StakingKeeper.GetHistoricalInfo(ctx, 5) + suite.True(found) + + var req *types.QueryHistoricalInfoRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryHistoricalInfoRequest{} + }, + false, + }, + { + "invalid request with negative height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: -1} + }, + false, + }, + { + "valid request with old height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: 4} + }, + false, + }, + { + "valid request with current height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: 5} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.HistoricalInfo(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.NotNil(res) + suite.True(hi.Equal(res.Hist)) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryRedelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + + addrAcc, addrAcc1 := addrs[0], addrs[1] + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + val1, val2, val3, val4 := vals[0], vals[1], valAddrs[3], valAddrs[4] + delAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) + _, err := app.StakingKeeper.Delegate(ctx, addrAcc1, delAmount, types.Unbonded, val1, true) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + rdAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator(), sdk.NewDecFromInt(rdAmount)) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator()) + suite.True(found) + + var req *types.QueryRedelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + { + "request redelegations for non existent addr", + func() { + req = &types.QueryRedelegationsRequest{DelegatorAddr: addrAcc.String()} + }, + false, + false, + }, + { + "request redelegations with non existent pairs", + func() { + req = &types.QueryRedelegationsRequest{ + DelegatorAddr: addrAcc.String(), SrcValidatorAddr: val3.String(), + DstValidatorAddr: val4.String(), + } + }, + false, + true, + }, + { + "request redelegations with delegatoraddr, sourceValAddr, destValAddr", + func() { + req = &types.QueryRedelegationsRequest{ + DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, + DstValidatorAddr: val2.OperatorAddress, Pagination: &query.PageRequest{}, + } + }, + true, + false, + }, + { + "request redelegations with delegatoraddr and sourceValAddr", + func() { + req = &types.QueryRedelegationsRequest{ + DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, + Pagination: &query.PageRequest{}, + } + }, + true, + false, + }, + { + "query redelegations with sourceValAddr only", + func() { + req = &types.QueryRedelegationsRequest{ + SrcValidatorAddr: val1.GetOperator().String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.Redelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Len(res.RedelegationResponses, len(redel.Entries)) + suite.Equal(redel.DelegatorAddress, res.RedelegationResponses[0].Redelegation.DelegatorAddress) + suite.Equal(redel.ValidatorSrcAddress, res.RedelegationResponses[0].Redelegation.ValidatorSrcAddress) + suite.Equal(redel.ValidatorDstAddress, res.RedelegationResponses[0].Redelegation.ValidatorDstAddress) + suite.Len(redel.Entries, len(res.RedelegationResponses[0].Entries)) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.RedelegationResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *IntegrationTestSuite) TestGRPCQueryValidatorUnbondingDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc1, _ := addrs[0], addrs[1] + val1 := vals[0] + + // undelegate + undelAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), sdk.NewDecFromInt(undelAmount)) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + var req *types.QueryValidatorUnbondingDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorUnbondingDelegationsRequest{} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: val1.GetOperator().String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.ValidatorUnbondingDelegations(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(uint64(1), res.Pagination.Total) + suite.Equal(1, len(res.UnbondingResponses)) + suite.Equal(res.UnbondingResponses[0].ValidatorAddress, val1.OperatorAddress) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} diff --git a/tests/integration/staking/keeper/keeper_test.go b/tests/integration/staking/keeper/keeper_test.go new file mode 100644 index 0000000000000..6361f688b6e4f --- /dev/null +++ b/tests/integration/staking/keeper/keeper_test.go @@ -0,0 +1,76 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + addrs []sdk.AccAddress + vals []types.Validator + queryClient types.QueryClient + msgServer types.MsgServer +} + +func (suite *IntegrationTestSuite) SetupTest() { + app := simapp.Setup(suite.T(), false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + querier := keeper.Querier{Keeper: app.StakingKeeper} + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, querier) + queryClient := types.NewQueryClient(queryHelper) + + suite.msgServer = keeper.NewMsgServerImpl(app.StakingKeeper) + + addrs, _, validators := createValidators(suite.T(), ctx, app, []int64{9, 8, 7}) + header := tmproto.Header{ + ChainID: "HelloChain", + Height: 5, + } + + // sort a copy of the validators, so that original validators does not + // have its order changed + sortedVals := make([]types.Validator, len(validators)) + copy(sortedVals, validators) + hi := types.NewHistoricalInfo(header, sortedVals, app.StakingKeeper.PowerReduction(ctx)) + app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi) + + suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals = app, ctx, queryClient, addrs, validators +} + +func TestParams(t *testing.T) { + app := simapp.Setup(t, false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + expParams := types.DefaultParams() + + // check that the empty keeper loads the default + resParams := app.StakingKeeper.GetParams(ctx) + require.True(t, expParams.Equal(resParams)) + + // modify a params, save, and retrieve + expParams.MaxValidators = 777 + app.StakingKeeper.SetParams(ctx, expParams) + resParams = app.StakingKeeper.GetParams(ctx) + require.True(t, expParams.Equal(resParams)) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/tests/integration/staking/keeper/msg_server_test.go b/tests/integration/staking/keeper/msg_server_test.go new file mode 100644 index 0000000000000..44538be4d4aea --- /dev/null +++ b/tests/integration/staking/keeper/msg_server_test.go @@ -0,0 +1,146 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +func TestCancelUnbondingDelegation(t *testing.T) { + // setup the app + app := simapp.Setup(t, false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + msgServer := keeper.NewMsgServerImpl(app.StakingKeeper) + bondDenom := app.StakingKeeper.BondDenom(ctx) + + // set the not bonded pool module account + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 5) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + moduleBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, sdk.NewInt64Coin(bondDenom, startTokens.Int64()), moduleBalance) + + // accounts + delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000)) + validators := app.StakingKeeper.GetValidators(ctx, 10) + require.Equal(t, len(validators), 1) + + validatorAddr, err := sdk.ValAddressFromBech32(validators[0].OperatorAddress) + require.NoError(t, err) + delegatorAddr := delAddrs[0] + + // setting the ubd entry + unbondingAmount := sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 5) + ubd := types.NewUnbondingDelegation( + delegatorAddr, validatorAddr, 10, + ctx.BlockTime().Add(time.Minute*10), + unbondingAmount.Amount, + ) + + // set and retrieve a record + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + resUnbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + require.True(t, found) + require.Equal(t, ubd, resUnbond) + + testCases := []struct { + Name string + ExceptErr bool + req types.MsgCancelUnbondingDelegation + }{ + { + Name: "invalid height", + ExceptErr: true, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(4)), + CreationHeight: 0, + }, + }, + { + Name: "invalid coin", + ExceptErr: true, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: sdk.NewCoin("dump_coin", sdk.NewInt(4)), + CreationHeight: 0, + }, + }, + { + Name: "validator not exists", + ExceptErr: true, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: sdk.ValAddress(sdk.AccAddress("asdsad")).String(), + Amount: unbondingAmount, + CreationHeight: 0, + }, + }, + { + Name: "invalid delegator address", + ExceptErr: true, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: "invalid_delegator_addrtess", + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: unbondingAmount, + CreationHeight: 0, + }, + }, + { + Name: "invalid amount", + ExceptErr: true, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: unbondingAmount.Add(sdk.NewInt64Coin(bondDenom, 10)), + CreationHeight: 10, + }, + }, + { + Name: "success", + ExceptErr: false, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1)), + CreationHeight: 10, + }, + }, + { + Name: "success", + ExceptErr: false, + req: types.MsgCancelUnbondingDelegation{ + DelegatorAddress: resUnbond.DelegatorAddress, + ValidatorAddress: resUnbond.ValidatorAddress, + Amount: unbondingAmount.Sub(unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1))), + CreationHeight: 10, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Name, func(t *testing.T) { + _, err := msgServer.CancelUnbondingDelegation(ctx, &testCase.req) + if testCase.ExceptErr { + require.Error(t, err) + } else { + require.NoError(t, err) + balanceForNotBondedPool := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(notBondedPool.GetAddress()), bondDenom) + require.Equal(t, balanceForNotBondedPool, moduleBalance.Sub(testCase.req.Amount)) + moduleBalance = moduleBalance.Sub(testCase.req.Amount) + } + }) + } +} diff --git a/tests/integration/staking/keeper/slash_test.go b/tests/integration/staking/keeper/slash_test.go new file mode 100644 index 0000000000000..4a09ff165deb0 --- /dev/null +++ b/tests/integration/staking/keeper/slash_test.go @@ -0,0 +1,585 @@ +package keeper_test + +import ( + "testing" + "time" + + "cosmossdk.io/math" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// bootstrapSlashTest creates 3 validators and bootstrap the app. +func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { + _, app, ctx := createTestInput(t) + + addrDels, addrVals := generateAddresses(app, ctx, 100) + + amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) + + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) + + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + numVals := int64(3) + bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals))) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + + // set bonded pool balance + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins)) + + for i := int64(0); i < numVals; i++ { + validator := teststaking.NewValidator(t, addrVals[i], PKs[i]) + validator, _ = validator.AddTokensFromDel(amt) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + } + + return app, ctx, addrDels, addrVals +} + +// tests slashUnbondingDelegation +func TestSlashUnbondingDelegation(t *testing.T) { + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + + fraction := sdk.NewDecWithPrec(5, 1) + + // set an unbonding delegation with expiration timestamp (beyond which the + // unbonding delegation shouldn't be slashed) + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, + time.Unix(5, 0), sdk.NewInt(10)) + + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + + // unbonding started prior to the infraction height, stakw didn't contribute + slashAmount := app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 1, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) + + // after the expiration time, no longer eligible for slashing + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) + + // test valid slash, before expiration timestamp and to which stake contributed + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + oldUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(5))) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // initial balance unchanged + require.Equal(t, sdk.NewInt(10), ubd.Entries[0].InitialBalance) + + // balance decreased + require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance) + newUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + diffTokens := oldUnbondedPoolBalances.Sub(newUnbondedPoolBalances...) + require.True(t, diffTokens.AmountOf(app.StakingKeeper.BondDenom(ctx)).Equal(sdk.NewInt(5))) +} + +// tests slashRedelegation +func TestSlashRedelegation(t *testing.T) { + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) + + // add bonded tokens to pool for (re)delegations + startCoins := sdk.NewCoins(sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 15)) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), startCoins)) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + // set a redelegation with an expiration timestamp beyond which the + // redelegation shouldn't be slashed + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + time.Unix(5, 0), sdk.NewInt(10), math.LegacyNewDec(10)) + + app.StakingKeeper.SetRedelegation(ctx, rd) + + // set the associated delegation + del := types.NewDelegation(addrDels[0], addrVals[1], math.LegacyNewDec(10)) + app.StakingKeeper.SetDelegation(ctx, del) + + // started redelegating prior to the current height, stake didn't contribute to infraction + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount := app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 1, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) + + // after the expiration time, no longer eligible for slashing + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) + app.StakingKeeper.SetRedelegation(ctx, rd) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) + + balances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + // test valid slash, before expiration timestamp and to which stake contributed + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) + app.StakingKeeper.SetRedelegation(ctx, rd) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(5))) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + + // end block + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + + // initialbalance unchanged + require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance) + + // shares decreased + del, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[1]) + require.True(t, found) + require.Equal(t, int64(5), del.Shares.RoundInt64()) + + // pool bonded tokens should decrease + burnedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), slashAmount)) + require.Equal(t, balances.Sub(burnedCoins...), app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())) +} + +// test slash at a negative height +// this just represents pre-genesis and should have the same effect as slashing at height 0 +func TestSlashAtNegativeHeight(t *testing.T) { + app, ctx, _, _ := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + app.StakingKeeper.Slash(ctx, consAddr, -2, 10, fraction) + + // read updated state + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + // end block + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + + validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + require.True(t, found) + // power decreased + require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // pool bonded shares decreased + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String()) +} + +// tests Slash at the current height +func TestSlashValidatorAtCurrentHeight(t *testing.T) { + app, ctx, _, _ := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) + + // read updated state + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + // end block + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + + validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + assert.True(t, found) + // power decreased + require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // pool bonded shares decreased + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String()) +} + +// tests Slash at a previous height with an unbonding delegation +func TestSlashWithUnbondingDelegation(t *testing.T) { + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + // set an unbonding delegation with expiration timestamp beyond which the + // unbonding delegation shouldn't be slashed + ubdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdTokens) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + + // slash validator for the first time + ctx = ctx.WithBlockHeight(12) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) + + // end block + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + + // read updating unbonding delegation + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // balance decreased + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 2), ubd.Entries[0].Balance) + + // bonded tokens burned + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 3), diffTokens) + + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + // power decreased by 3 - 6 stake originally bonded at the time of infraction + // was still bonded at the time of discovery and was slashed by half, 4 stake + // bonded at the time of discovery hadn't been bonded at the time of infraction + // and wasn't slashed + require.Equal(t, int64(7), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // slash validator again + ctx = ctx.WithBlockHeight(13) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // balance decreased again + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + + // bonded tokens burned again + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 6), diffTokens) + + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + // power decreased by 3 again + require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // slash validator again + // all originally bonded stake has been slashed, so this will have no effect + // on the unbonding delegation, but it will slash stake bonded since the infraction + // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 + ctx = ctx.WithBlockHeight(13) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // balance unchanged + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + + // bonded tokens burned again + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), diffTokens) + + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + // power decreased by 3 again + require.Equal(t, int64(1), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // slash validator again + // all originally bonded stake has been slashed, so this will have no effect + // on the unbonding delegation, but it will slash stake bonded since the infraction + // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 + ctx = ctx.WithBlockHeight(13) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // balance unchanged + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + + // just 1 bonded token burned again since that's all the validator now has + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 10), diffTokens) + + // apply TM updates + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + + // read updated validator + // power decreased by 1 again, validator is out of stake + // validator should be in unbonding period + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) +} + +// tests Slash at a previous height with a redelegation +func TestSlashWithRedelegation(t *testing.T) { + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + bondDenom := app.StakingKeeper.BondDenom(ctx) + + // set a redelegation + rdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdTokens, sdk.NewDecFromInt(rdTokens)) + app.StakingKeeper.SetRedelegation(ctx, rd) + + // set the associated delegation + del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdTokens)) + app.StakingKeeper.SetDelegation(ctx, del) + + // update bonded tokens + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2))) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), rdCoins)) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + + // slash validator + ctx = ctx.WithBlockHeight(12) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) }) + burnAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt() + + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + // burn bonded tokens from only from delegations + bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + + notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + + // read updating redelegation + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 2 - 4 stake originally bonded at the time of infraction + // was still bonded at the time of discovery and was slashed by half, 4 stake + // bonded at the time of discovery hadn't been bonded at the time of infraction + // and wasn't slashed + require.Equal(t, int64(8), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // slash the validator again + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) + burnAmount = app.StakingKeeper.TokensFromConsensusPower(ctx, 7) + + // read updated pool + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + // seven bonded tokens burned + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + + // read updating redelegation + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 4 + require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + + // slash the validator again, by 100% + ctx = ctx.WithBlockHeight(12) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) + + burnAmount = sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(math.LegacyOneDec()).TruncateInt() + burnAmount = burnAmount.Sub(math.LegacyOneDec().MulInt(rdTokens).TruncateInt()) + + // read updated pool + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + + // read updating redelegation + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // apply TM updates + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + // read updated validator + // validator decreased to zero power, should be in unbonding period + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) + + // slash the validator again, by 100% + // no stake remains to be slashed + ctx = ctx.WithBlockHeight(12) + // validator still in unbonding period + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) + + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) + + // read updated pool + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded, bondedPoolBalance)) + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) + + // read updating redelegation + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + // power still zero, still in unbonding period + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) +} + +// tests Slash at a previous height with both an unbonding delegation and a redelegation +func TestSlashBoth(t *testing.T) { + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) + bondDenom := app.StakingKeeper.BondDenom(ctx) + + // set a redelegation with expiration timestamp beyond which the + // redelegation shouldn't be slashed + rdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) + rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdATokens, sdk.NewDecFromInt(rdATokens)) + app.StakingKeeper.SetRedelegation(ctx, rdA) + + // set the associated delegation + delA := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdATokens)) + app.StakingKeeper.SetDelegation(ctx, delA) + + // set an unbonding delegation with expiration timestamp (beyond which the + // unbonding delegation shouldn't be slashed) + ubdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) + ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, + time.Unix(0, 0), ubdATokens) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubdA) + + bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2))) + notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens)) + + // update bonded tokens + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins)) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), notBondedCoins)) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + // slash validator + ctx = ctx.WithBlockHeight(12) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + app.StakingKeeper.Slash(ctx, consAddr0, 10, 10, fraction) + + burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt() + burnedBondAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt() + burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount) + + // read updated pool + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPoolBalance)) + + notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(math.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPoolBalance)) + + // read updating redelegation + rdA, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rdA.Entries, 1) + // read updated validator + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + // power not decreased, all stake was bonded since + require.Equal(t, int64(10), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) +} + +func TestSlashAmount(t *testing.T) { + app, ctx, _, _ := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + burnedCoins := app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) + require.True(t, burnedCoins.GT(math.ZeroInt())) + + // test the case where the validator was not found, which should return no coins + _, addrVals := generateAddresses(app, ctx, 100) + noBurned := app.StakingKeeper.Slash(ctx, sdk.ConsAddress(addrVals[0]), ctx.BlockHeight(), 10, fraction) + require.True(t, sdk.NewInt(0).Equal(noBurned)) +} diff --git a/x/staking/keeper/validator_bench_test.go b/tests/integration/staking/keeper/validator_bench_test.go similarity index 100% rename from x/staking/keeper/validator_bench_test.go rename to tests/integration/staking/keeper/validator_bench_test.go diff --git a/tests/integration/staking/keeper/validator_test.go b/tests/integration/staking/keeper/validator_test.go new file mode 100644 index 0000000000000..82f1caea9b19f --- /dev/null +++ b/tests/integration/staking/keeper/validator_test.go @@ -0,0 +1,834 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func newMonikerValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey, moniker string) types.Validator { + v, err := types.NewValidator(operator, pubKey, types.Description{Moniker: moniker}) + require.NoError(t, err) + return v +} + +func bootstrapValidatorTest(t testing.TB, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { + _, app, ctx := createTestInput(&testing.T{}) + + addrDels, addrVals := generateAddresses(app, ctx, numAddrs) + + amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) + + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + // set bonded pool supply + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) + + // unbond genesis validator delegations + delegations := app.StakingKeeper.GetAllDelegations(ctx) + require.Len(t, delegations, 1) + delegation := delegations[0] + + _, err := app.StakingKeeper.Undelegate(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr(), delegation.Shares) + require.NoError(t, err) + + // end block to unbond genesis validator + staking.EndBlocker(ctx, app.StakingKeeper) + + return app, ctx, addrDels, addrVals +} + +func initValidators(t testing.TB, power int64, numAddrs int, powers []int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress, []types.Validator) { + app, ctx, addrs, valAddrs := bootstrapValidatorTest(t, power, numAddrs) + pks := simtestutil.CreateTestPubKeys(numAddrs) + + vs := make([]types.Validator, len(powers)) + for i, power := range powers { + vs[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), pks[i]) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + vs[i], _ = vs[i].AddTokensFromDel(tokens) + } + return app, ctx, addrs, valAddrs, vs +} + +func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { + numVals := 10 + maxVals := 5 + + // create context, keeper, and pool for tests + app, ctx, _, valAddrs := bootstrapValidatorTest(t, 0, 100) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + + // create keeper parameters + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(maxVals) + app.StakingKeeper.SetParams(ctx, params) + + // create a random pool + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 1234))))) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 10000))))) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + validators := make([]types.Validator, numVals) + for i := 0; i < len(validators); i++ { + moniker := fmt.Sprintf("val#%d", int64(i)) + val := newMonikerValidator(t, valAddrs[i], PKs[i], moniker) + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, int64((i+1)*10)) + val, _ = val.AddTokensFromDel(delTokens) + + val = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, val, true) + validators[i] = val + } + + nextCliffVal := validators[numVals-maxVals+1] + + // remove enough tokens to kick out the validator below the current cliff + // validator and next in line cliff validator + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) + shares := app.StakingKeeper.TokensFromConsensusPower(ctx, 21) + nextCliffVal, _ = nextCliffVal.RemoveDelShares(sdk.NewDecFromInt(shares)) + nextCliffVal = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, nextCliffVal, true) + + expectedValStatus := map[int]types.BondStatus{ + 9: types.Bonded, 8: types.Bonded, 7: types.Bonded, 5: types.Bonded, 4: types.Bonded, + 0: types.Unbonding, 1: types.Unbonding, 2: types.Unbonding, 3: types.Unbonding, 6: types.Unbonding, + } + + // require all the validators have their respective statuses + for valIdx, status := range expectedValStatus { + valAddr := validators[valIdx].OperatorAddress + addr, err := sdk.ValAddressFromBech32(valAddr) + assert.NoError(t, err) + val, _ := app.StakingKeeper.GetValidator(ctx, addr) + + assert.Equal( + t, status, val.GetStatus(), + fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status), + ) + } +} + +func TestSlashToZeroPowerRemoved(t *testing.T) { + // initialize setup + app, ctx, _, addrVals := bootstrapValidatorTest(t, 100, 20) + + // add a validator + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens)))) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + validator, _ = validator.AddTokensFromDel(valTokens) + require.Equal(t, types.Unbonded, validator.Status) + require.Equal(t, valTokens, validator.Tokens) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, valTokens) + + // slash the validator by 100% + app.StakingKeeper.Slash(ctx, sdk.ConsAddress(PKs[0].Address()), 0, 100, math.LegacyOneDec()) + // apply TM updates + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + // validator should be unbonding + validator, _ = app.StakingKeeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), types.Unbonding) +} + +// test how the validators are sorted, tests GetBondedValidatorsByPower +func TestGetValidatorSortingUnmixed(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + + // initialize some validators into the state + amts := []math.Int{ + sdk.NewIntFromUint64(0), + app.StakingKeeper.PowerReduction(ctx).MulRaw(100), + app.StakingKeeper.PowerReduction(ctx), + app.StakingKeeper.PowerReduction(ctx).MulRaw(400), + app.StakingKeeper.PowerReduction(ctx).MulRaw(200), + } + n := len(amts) + var validators [5]types.Validator + for i, amt := range amts { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + validators[i].Status = types.Bonded + validators[i].Tokens = amt + validators[i].DelegatorShares = sdk.NewDecFromInt(amt) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) + } + + // first make sure everything made it in to the gotValidator group + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, n, len(resValidators)) + assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(100).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(1).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[1].OperatorAddress, resValidators[2].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[2].OperatorAddress, resValidators[3].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[0].OperatorAddress, resValidators[4].OperatorAddress, "%v", resValidators) + + // test a basic increase in voting power + validators[3].Tokens = sdk.NewInt(500).Mul(app.StakingKeeper.PowerReduction(ctx)) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + + // test a decrease in voting power + validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // test equal voting power, different age + validators[3].Tokens = sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)) + ctx = ctx.WithBlockHeight(10) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // no change in voting power - no change in sort + ctx = ctx.WithBlockHeight(20) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // change in voting power of both validators, both still in v-set, no age change + validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) + validators[4].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + ctx = ctx.WithBlockHeight(30) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n, "%v", resValidators) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) +} + +func TestGetValidatorSortingMixed(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 501))))) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 0))))) + + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + // now 2 max resValidators + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = 2 + app.StakingKeeper.SetParams(ctx, params) + + // initialize some validators into the state + amts := []math.Int{ + sdk.NewIntFromUint64(0), + app.StakingKeeper.PowerReduction(ctx).MulRaw(100), + app.StakingKeeper.PowerReduction(ctx), + app.StakingKeeper.PowerReduction(ctx).MulRaw(400), + app.StakingKeeper.PowerReduction(ctx).MulRaw(200), + } + + var validators [5]types.Validator + for i, amt := range amts { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + validators[i].DelegatorShares = sdk.NewDecFromInt(amt) + validators[i].Status = types.Bonded + validators[i].Tokens = amt + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) + } + + val0, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0])) + require.True(t, found) + val1, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1])) + require.True(t, found) + val2, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[2])) + require.True(t, found) + val3, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[3])) + require.True(t, found) + val4, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[4])) + require.True(t, found) + require.Equal(t, types.Bonded, val0.Status) + require.Equal(t, types.Unbonding, val1.Status) + require.Equal(t, types.Unbonding, val2.Status) + require.Equal(t, types.Bonded, val3.Status) + require.Equal(t, types.Bonded, val4.Status) + + // first make sure everything made it in to the gotValidator group + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + // The validators returned should match the max validators + assert.Equal(t, 2, len(resValidators)) + assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) +} + +// TODO separate out into multiple tests +func TestGetValidatorsEdgeCases(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + + // set max validators to 2 + params := app.StakingKeeper.GetParams(ctx) + nMax := uint32(2) + params.MaxValidators = nMax + app.StakingKeeper.SetParams(ctx, params) + + // initialize some validators into the state + powers := []int64{0, 100, 400, 400} + var validators [4]types.Validator + for i, power := range powers { + moniker := fmt.Sprintf("val#%d", int64(i)) + validators[i] = newMonikerValidator(t, sdk.ValAddress(addrs[i]), PKs[i], moniker) + + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, tokens)))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + validators[i] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) + } + + // ensure that the first two bonded validators are the largest validators + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) + assert.True(ValEq(t, validators[2], resValidators[0])) + assert.True(ValEq(t, validators[3], resValidators[1])) + + // delegate 500 tokens to validator 0 + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500) + validators[0], _ = validators[0].AddTokensFromDel(delTokens) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + + newTokens := sdk.NewCoins() + + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens)) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + // test that the two largest validators are + // a) validator 0 with 500 tokens + // b) validator 2 with 400 tokens (delegated before validator 3) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + + // A validator which leaves the bonded validator set due to a decrease in voting power, + // then increases to the original voting power, does not get its spot back in the + // case of a tie. + // + // Order of operations for this test: + // - validator 3 enter validator set with 1 new token + // - validator 3 removed validator set by removing 201 tokens (validator 2 enters) + // - validator 3 adds 200 tokens (equal to validator 2 now) and does not get its spot back + + // validator 3 enters bonded validator set + ctx = ctx.WithBlockHeight(40) + + var found bool + validators[3], found = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) + assert.True(t, found) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + validators[3], _ = validators[3].AddTokensFromDel(app.StakingKeeper.TokensFromConsensusPower(ctx, 1)) + + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 1))) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens)) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[3], resValidators[1])) + + // validator 3 kicked out temporarily + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + rmTokens := validators[3].TokensFromShares(math.LegacyNewDec(201)).TruncateInt() + validators[3], _ = validators[3].RemoveDelShares(math.LegacyNewDec(201)) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, rmTokens)))) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + + // validator 3 does not get spot back + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200)) + + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.NewInt(200))))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + _, exists := app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) + require.True(t, exists) +} + +func TestValidatorBondHeight(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + + // now 2 max resValidators + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = 2 + app.StakingKeeper.SetParams(ctx, params) + + // initialize some validators into the state + var validators [3]types.Validator + validators[0] = teststaking.NewValidator(t, sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) + validators[1] = teststaking.NewValidator(t, sdk.ValAddress(addrs[1]), PKs[1]) + validators[2] = teststaking.NewValidator(t, sdk.ValAddress(addrs[2]), PKs[2]) + + tokens0 := app.StakingKeeper.TokensFromConsensusPower(ctx, 200) + tokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) + tokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) + validators[0], _ = validators[0].AddTokensFromDel(tokens0) + validators[1], _ = validators[1].AddTokensFromDel(tokens1) + validators[2], _ = validators[2].AddTokensFromDel(tokens2) + + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + + //////////////////////////////////////// + // If two validators both increase to the same voting power in the same block, + // the one with the first transaction should become bonded + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) + + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, uint32(len(resValidators)), params.MaxValidators) + + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[1], resValidators[1])) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[2]) + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 50) + validators[1], _ = validators[1].AddTokensFromDel(delTokens) + validators[2], _ = validators[2].AddTokensFromDel(delTokens) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, params.MaxValidators, uint32(len(resValidators))) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) +} + +func TestFullValidatorSetPowerChange(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) + max := 2 + params.MaxValidators = uint32(2) + app.StakingKeeper.SetParams(ctx, params) + + // initialize some validators into the state + powers := []int64{0, 100, 400, 400, 200} + var validators [5]types.Validator + for i, power := range powers { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) + } + for i := range powers { + var found bool + validators[i], found = app.StakingKeeper.GetValidator(ctx, validators[i].GetOperator()) + require.True(t, found) + } + assert.Equal(t, types.Unbonded, validators[0].Status) + assert.Equal(t, types.Unbonding, validators[1].Status) + assert.Equal(t, types.Bonded, validators[2].Status) + assert.Equal(t, types.Bonded, validators[3].Status) + assert.Equal(t, types.Unbonded, validators[4].Status) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs + assert.True(ValEq(t, validators[3], resValidators[1])) + + // test a swap in voting power + + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 600) + validators[0], _ = validators[0].AddTokensFromDel(tokens) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) +} + +func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + } + + // test from nothing to something + // tendermintUpdate set: {} -> {c1, c3} + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.SetValidator(ctx, validators[1]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) + + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) + assert.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) + assert.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + + // test identical, + // tendermintUpdate set: {} -> {} + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) +} + +func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + + // test single value change + // tendermintUpdate set: {} -> {c1'} + validators[0].Status = types.Bonded + validators[0].Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 600) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { + powers := []int64{10, 20} + // TODO: use it in other places + app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) + + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + + // test multiple value change + // tendermintUpdate set: {c1, c3} -> {c1', c3'} + delTokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 190) + delTokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 80) + validators[0], _ = validators[0].AddTokensFromDel(delTokens1) + validators[1], _ = validators[1].AddTokensFromDel(delTokens2) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) +} + +func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { + powers := []int64{10, 20, 5, 15, 25} + app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) + + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + + // test validtor added at the beginning + // tendermintUpdate set: {} -> {c0} + app.StakingKeeper.SetValidator(ctx, validators[2]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) + require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + + // test validtor added at the beginning + // tendermintUpdate set: {} -> {c0} + app.StakingKeeper.SetValidator(ctx, validators[3]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[3]) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[3], _ = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) + require.Equal(t, validators[3].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + + // test validtor added at the end + // tendermintUpdate set: {} -> {c0} + app.StakingKeeper.SetValidator(ctx, validators[4]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[4]) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[4], _ = app.StakingKeeper.GetValidator(ctx, validators[4].GetOperator()) + require.Equal(t, validators[4].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + params := types.DefaultParams() + params.MaxValidators = 2 + app.StakingKeeper.SetParams(ctx, params) + + powers := []int64{10, 20, 5} + var validators [5]types.Validator + for i, power := range powers { + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + } + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + + // test validator added at the end but not inserted in the valset + // tendermintUpdate set: {} -> {} + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + // test validator change its power and become a gotValidator (pushing out an existing) + // tendermintUpdate set: {} -> {c0, c4} + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + validators[2], _ = validators[2].AddTokensFromDel(tokens) + app.StakingKeeper.SetValidator(ctx, validators[2]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) + require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1]) + require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(3) + + app.StakingKeeper.SetParams(ctx, params) + + powers := []int64{100, 100} + var validators [2]types.Validator + + // initialize some validators into the state + for i, power := range powers { + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) + require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) + + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + // update initial validator set + for i, power := range powers { + + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[i]) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // add a new validator that goes from zero power, to non-zero power, back to + // zero power + valPubKey := PKs[len(validators)+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + amt := sdk.NewInt(100) + + validator := teststaking.NewValidator(t, valAddr, valPubKey) + validator, _ = validator.AddTokensFromDel(amt) + + app.StakingKeeper.SetValidator(ctx, validator) + + validator, _ = validator.RemoveDelShares(sdk.NewDecFromInt(amt)) + app.StakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) + + // add a new validator that increases in power + valPubKey = PKs[len(validators)+2] + valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) + + validator = teststaking.NewValidator(t, valAddr, valPubKey) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500) + validator, _ = validator.AddTokensFromDel(tokens) + app.StakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) + + // verify initial Tendermint updates are correct + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)+1) + validator, _ = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) + require.Equal(t, validator.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) + require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[2]) +} + +func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(2) + + app.StakingKeeper.SetParams(ctx, params) + + powers := []int64{100, 200, 300} + var validators [3]types.Validator + + // initialize some validators into the state + for i, power := range powers { + moniker := fmt.Sprintf("%d", i) + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = newMonikerValidator(t, valAddr, valPubKey, moniker) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) + require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) + + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + // delegate to validator with lowest power but not enough to bond + ctx = ctx.WithBlockHeight(1) + + var found bool + validators[0], found = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + require.True(t, found) + + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) + validators[0], _ = validators[0].AddTokensFromDel(tokens) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) + + // verify initial Tendermint updates are correct + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + // create a series of events that will bond and unbond the validator with + // lowest power in a single block context (height) + ctx = ctx.WithBlockHeight(2) + + validators[1], found = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) + require.True(t, found) + + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + validators[0], _ = validators[0].RemoveDelShares(validators[0].DelegatorShares) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 250) + validators[1], _ = validators[1].AddTokensFromDel(tokens) + app.StakingKeeper.SetValidator(ctx, validators[1]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) + + // verify initial Tendermint updates are correct + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) +} + +func applyValidatorSetUpdates(t *testing.T, ctx sdk.Context, k *keeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate { + updates, err := k.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) + if expectedUpdatesLen >= 0 { + require.Equal(t, expectedUpdatesLen, len(updates), "%v", updates) + } + return updates +} diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index f93617a3bf2f4..0102898bb2158 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -1,160 +1,152 @@ package keeper_test import ( - "testing" "time" "cosmossdk.io/math" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/golang/mock/gomock" - "github.com/cosmos/cosmos-sdk/simapp" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - "github.com/cosmos/cosmos-sdk/x/staking/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations -func TestDelegation(t *testing.T) { - _, app, ctx := createTestInput(t) +func createValAddrs(count int) ([]sdk.AccAddress, []sdk.ValAddress) { + addrs := simtestutil.CreateIncrementalAccounts(count) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - // remove genesis validator delegations - delegations := app.StakingKeeper.GetAllDelegations(ctx) - require.Len(t, delegations, 1) + return addrs, valAddrs +} - app.StakingKeeper.RemoveDelegation(ctx, types.Delegation{ - ValidatorAddress: delegations[0].ValidatorAddress, - DelegatorAddress: delegations[0].DelegatorAddress, - }) +// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations +func (s *KeeperTestSuite) TestDelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(10000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) + addrDels, valAddrs := createValAddrs(3) // construct the validators amts := []math.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)} - var validators [3]types.Validator + var validators [3]stakingtypes.Validator for i, amt := range amts { - validators[i] = teststaking.NewValidator(t, valAddrs[i], PKs[i]) + validators[i] = teststaking.NewValidator(s.T(), valAddrs[i], PKs[i]) validators[i], _ = validators[i].AddTokensFromDel(amt) - } - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) - validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) + validators[i] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[i], true) + } // first add a validators[0] to delegate too - bond1to1 := types.NewDelegation(addrDels[0], valAddrs[0], math.LegacyNewDec(9)) + bond1to1 := stakingtypes.NewDelegation(addrDels[0], valAddrs[0], math.LegacyNewDec(9)) // check the empty keeper first - _, found := app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) - require.False(t, found) + _, found := keeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) + require.False(found) // set and retrieve a record - app.StakingKeeper.SetDelegation(ctx, bond1to1) - resBond, found := app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) - require.True(t, found) - require.Equal(t, bond1to1, resBond) + keeper.SetDelegation(ctx, bond1to1) + resBond, found := keeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) + require.True(found) + require.Equal(bond1to1, resBond) // modify a records, save, and retrieve bond1to1.Shares = math.LegacyNewDec(99) - app.StakingKeeper.SetDelegation(ctx, bond1to1) - resBond, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) - require.True(t, found) - require.Equal(t, bond1to1, resBond) + keeper.SetDelegation(ctx, bond1to1) + resBond, found = keeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) + require.True(found) + require.Equal(bond1to1, resBond) // add some more records - bond1to2 := types.NewDelegation(addrDels[0], valAddrs[1], math.LegacyNewDec(9)) - bond1to3 := types.NewDelegation(addrDels[0], valAddrs[2], math.LegacyNewDec(9)) - bond2to1 := types.NewDelegation(addrDels[1], valAddrs[0], math.LegacyNewDec(9)) - bond2to2 := types.NewDelegation(addrDels[1], valAddrs[1], math.LegacyNewDec(9)) - bond2to3 := types.NewDelegation(addrDels[1], valAddrs[2], math.LegacyNewDec(9)) - app.StakingKeeper.SetDelegation(ctx, bond1to2) - app.StakingKeeper.SetDelegation(ctx, bond1to3) - app.StakingKeeper.SetDelegation(ctx, bond2to1) - app.StakingKeeper.SetDelegation(ctx, bond2to2) - app.StakingKeeper.SetDelegation(ctx, bond2to3) + bond1to2 := stakingtypes.NewDelegation(addrDels[0], valAddrs[1], math.LegacyNewDec(9)) + bond1to3 := stakingtypes.NewDelegation(addrDels[0], valAddrs[2], math.LegacyNewDec(9)) + bond2to1 := stakingtypes.NewDelegation(addrDels[1], valAddrs[0], math.LegacyNewDec(9)) + bond2to2 := stakingtypes.NewDelegation(addrDels[1], valAddrs[1], math.LegacyNewDec(9)) + bond2to3 := stakingtypes.NewDelegation(addrDels[1], valAddrs[2], math.LegacyNewDec(9)) + keeper.SetDelegation(ctx, bond1to2) + keeper.SetDelegation(ctx, bond1to3) + keeper.SetDelegation(ctx, bond2to1) + keeper.SetDelegation(ctx, bond2to2) + keeper.SetDelegation(ctx, bond2to3) // test all bond retrieve capabilities - resBonds := app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[0], 5) - require.Equal(t, 3, len(resBonds)) - require.Equal(t, bond1to1, resBonds[0]) - require.Equal(t, bond1to2, resBonds[1]) - require.Equal(t, bond1to3, resBonds[2]) - resBonds = app.StakingKeeper.GetAllDelegatorDelegations(ctx, addrDels[0]) - require.Equal(t, 3, len(resBonds)) - resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[0], 2) - require.Equal(t, 2, len(resBonds)) - resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) - require.Equal(t, 3, len(resBonds)) - require.Equal(t, bond2to1, resBonds[0]) - require.Equal(t, bond2to2, resBonds[1]) - require.Equal(t, bond2to3, resBonds[2]) - allBonds := app.StakingKeeper.GetAllDelegations(ctx) - require.Equal(t, 6, len(allBonds)) - require.Equal(t, bond1to1, allBonds[0]) - require.Equal(t, bond1to2, allBonds[1]) - require.Equal(t, bond1to3, allBonds[2]) - require.Equal(t, bond2to1, allBonds[3]) - require.Equal(t, bond2to2, allBonds[4]) - require.Equal(t, bond2to3, allBonds[5]) - - resVals := app.StakingKeeper.GetDelegatorValidators(ctx, addrDels[0], 3) - require.Equal(t, 3, len(resVals)) - resVals = app.StakingKeeper.GetDelegatorValidators(ctx, addrDels[1], 4) - require.Equal(t, 3, len(resVals)) + resBonds := keeper.GetDelegatorDelegations(ctx, addrDels[0], 5) + require.Equal(3, len(resBonds)) + require.Equal(bond1to1, resBonds[0]) + require.Equal(bond1to2, resBonds[1]) + require.Equal(bond1to3, resBonds[2]) + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[0]) + require.Equal(3, len(resBonds)) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 2) + require.Equal(2, len(resBonds)) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(3, len(resBonds)) + require.Equal(bond2to1, resBonds[0]) + require.Equal(bond2to2, resBonds[1]) + require.Equal(bond2to3, resBonds[2]) + allBonds := keeper.GetAllDelegations(ctx) + require.Equal(6, len(allBonds)) + require.Equal(bond1to1, allBonds[0]) + require.Equal(bond1to2, allBonds[1]) + require.Equal(bond1to3, allBonds[2]) + require.Equal(bond2to1, allBonds[3]) + require.Equal(bond2to2, allBonds[4]) + require.Equal(bond2to3, allBonds[5]) + + resVals := keeper.GetDelegatorValidators(ctx, addrDels[0], 3) + require.Equal(3, len(resVals)) + resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4) + require.Equal(3, len(resVals)) for i := 0; i < 3; i++ { - resVal, err := app.StakingKeeper.GetDelegatorValidator(ctx, addrDels[0], valAddrs[i]) - require.Nil(t, err) - require.Equal(t, valAddrs[i], resVal.GetOperator()) + resVal, err := keeper.GetDelegatorValidator(ctx, addrDels[0], valAddrs[i]) + require.Nil(err) + require.Equal(valAddrs[i], resVal.GetOperator()) - resVal, err = app.StakingKeeper.GetDelegatorValidator(ctx, addrDels[1], valAddrs[i]) - require.Nil(t, err) - require.Equal(t, valAddrs[i], resVal.GetOperator()) + resVal, err = keeper.GetDelegatorValidator(ctx, addrDels[1], valAddrs[i]) + require.Nil(err) + require.Equal(valAddrs[i], resVal.GetOperator()) - resDels := app.StakingKeeper.GetValidatorDelegations(ctx, valAddrs[i]) - require.Len(t, resDels, 2) + resDels := keeper.GetValidatorDelegations(ctx, valAddrs[i]) + require.Len(resDels, 2) } // test total bonded for single delegator expBonded := bond1to1.Shares.Add(bond2to1.Shares).Add(bond1to3.Shares) - resDelBond := app.StakingKeeper.GetDelegatorBonded(ctx, addrDels[0]) - require.Equal(t, expBonded, sdk.NewDecFromInt(resDelBond)) + resDelBond := keeper.GetDelegatorBonded(ctx, addrDels[0]) + require.Equal(expBonded, sdk.NewDecFromInt(resDelBond)) // delete a record - app.StakingKeeper.RemoveDelegation(ctx, bond2to3) - _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[2]) - require.False(t, found) - resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) - require.Equal(t, 2, len(resBonds)) - require.Equal(t, bond2to1, resBonds[0]) - require.Equal(t, bond2to2, resBonds[1]) + keeper.RemoveDelegation(ctx, bond2to3) + _, found = keeper.GetDelegation(ctx, addrDels[1], valAddrs[2]) + require.False(found) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(2, len(resBonds)) + require.Equal(bond2to1, resBonds[0]) + require.Equal(bond2to2, resBonds[1]) - resBonds = app.StakingKeeper.GetAllDelegatorDelegations(ctx, addrDels[1]) - require.Equal(t, 2, len(resBonds)) + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[1]) + require.Equal(2, len(resBonds)) // delete all the records from delegator 2 - app.StakingKeeper.RemoveDelegation(ctx, bond2to1) - app.StakingKeeper.RemoveDelegation(ctx, bond2to2) - _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[0]) - require.False(t, found) - _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[1]) - require.False(t, found) - resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) - require.Equal(t, 0, len(resBonds)) + keeper.RemoveDelegation(ctx, bond2to1) + keeper.RemoveDelegation(ctx, bond2to2) + _, found = keeper.GetDelegation(ctx, addrDels[1], valAddrs[0]) + require.False(found) + _, found = keeper.GetDelegation(ctx, addrDels[1], valAddrs[1]) + require.False(found) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(0, len(resBonds)) } // tests Get/Set/Remove UnbondingDelegation -func TestUnbondingDelegation(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestUnbondingDelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(delAddrs) + delAddrs, valAddrs := createValAddrs(2) - ubd := types.NewUnbondingDelegation( + ubd := stakingtypes.NewUnbondingDelegation( delAddrs[0], valAddrs[0], 0, @@ -163,269 +155,154 @@ func TestUnbondingDelegation(t *testing.T) { ) // set and retrieve a record - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - resUnbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) - require.True(t, found) - require.Equal(t, ubd, resUnbond) + keeper.SetUnbondingDelegation(ctx, ubd) + resUnbond, found := keeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) + require.True(found) + require.Equal(ubd, resUnbond) // modify a records, save, and retrieve expUnbond := sdk.NewInt(21) ubd.Entries[0].Balance = expUnbond - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + keeper.SetUnbondingDelegation(ctx, ubd) - resUnbonds := app.StakingKeeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) - require.Equal(t, 1, len(resUnbonds)) + resUnbonds := keeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) + require.Equal(1, len(resUnbonds)) - resUnbonds = app.StakingKeeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) - require.Equal(t, 1, len(resUnbonds)) + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) + require.Equal(1, len(resUnbonds)) - resUnbond, found = app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) - require.True(t, found) - require.Equal(t, ubd, resUnbond) + resUnbond, found = keeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) + require.True(found) + require.Equal(ubd, resUnbond) - resDelUnbond := app.StakingKeeper.GetDelegatorUnbonding(ctx, delAddrs[0]) - require.Equal(t, expUnbond, resDelUnbond) + resDelUnbond := keeper.GetDelegatorUnbonding(ctx, delAddrs[0]) + require.Equal(expUnbond, resDelUnbond) // delete a record - app.StakingKeeper.RemoveUnbondingDelegation(ctx, ubd) - _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) - require.False(t, found) + keeper.RemoveUnbondingDelegation(ctx, ubd) + _, found = keeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) + require.False(found) - resUnbonds = app.StakingKeeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) - require.Equal(t, 0, len(resUnbonds)) + resUnbonds = keeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) + require.Equal(0, len(resUnbonds)) - resUnbonds = app.StakingKeeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) - require.Equal(t, 0, len(resUnbonds)) + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) + require.Equal(0, len(resUnbonds)) } -func TestUnbondDelegation(t *testing.T) { - _, app, ctx := createTestInput(t) - - delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(delAddrs) - - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)))) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) +func (s *KeeperTestSuite) TestUnbondDelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // create a validator and a delegator to that validator - // note this validator starts not-bonded - validator := teststaking.NewValidator(t, valAddrs[0], PKs[0]) + delAddrs, valAddrs := createValAddrs(1) + startTokens := keeper.TokensFromConsensusPower(ctx, 10) + validator := teststaking.NewValidator(s.T(), valAddrs[0], PKs[0]) validator, issuedShares := validator.AddTokensFromDel(startTokens) - require.Equal(t, startTokens, issuedShares.RoundInt()) + require.Equal(startTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + _ = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) - delegation := types.NewDelegation(delAddrs[0], valAddrs[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + delegation := stakingtypes.NewDelegation(delAddrs[0], valAddrs[0], issuedShares) + keeper.SetDelegation(ctx, delegation) - bondTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - amount, err := app.StakingKeeper.Unbond(ctx, delAddrs[0], valAddrs[0], sdk.NewDecFromInt(bondTokens)) - require.NoError(t, err) - require.Equal(t, bondTokens, amount) // shares to be added to an unbonding delegation + bondTokens := keeper.TokensFromConsensusPower(ctx, 6) + amount, err := keeper.Unbond(ctx, delAddrs[0], valAddrs[0], sdk.NewDecFromInt(bondTokens)) + require.NoError(err) + require.Equal(bondTokens, amount) // shares to be added to an unbonding delegation - delegation, found := app.StakingKeeper.GetDelegation(ctx, delAddrs[0], valAddrs[0]) - require.True(t, found) - validator, found = app.StakingKeeper.GetValidator(ctx, valAddrs[0]) - require.True(t, found) + delegation, found := keeper.GetDelegation(ctx, delAddrs[0], valAddrs[0]) + require.True(found) + validator, found = keeper.GetValidator(ctx, valAddrs[0]) + require.True(found) remainingTokens := startTokens.Sub(bondTokens) - require.Equal(t, remainingTokens, delegation.Shares.RoundInt()) - require.Equal(t, remainingTokens, validator.BondedTokens()) -} - -func TestUnbondingDelegationsMaxEntries(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - - bondDenom := app.StakingKeeper.BondDenom(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens)))) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - // create a validator and a delegator to that validator - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - - validator, issuedShares := validator.AddTokensFromDel(startTokens) - require.Equal(t, startTokens, issuedShares.RoundInt()) - - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(math.IntEq(t, startTokens, validator.BondedTokens())) - require.True(t, validator.IsBonded()) - - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) - - maxEntries := app.StakingKeeper.MaxEntries(ctx) - - oldBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - oldNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - - // should all pass - var completionTime time.Time - for i := int64(0); i < int64(maxEntries); i++ { - var err error - ctx = ctx.WithBlockHeight(i) - completionTime, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) - require.NoError(t, err) - } - - newBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - newNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(int64(maxEntries)))) - require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(int64(maxEntries)))) - - oldBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - - // an additional unbond should fail due to max entries - _, err := app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) - require.Error(t, err) - - newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - - require.True(math.IntEq(t, newBonded, oldBonded)) - require.True(math.IntEq(t, newNotBonded, oldNotBonded)) - // mature unbonding delegations - ctx = ctx.WithBlockTime(completionTime) - _, err = app.StakingKeeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) - require.NoError(t, err) - - newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, newBonded, oldBonded)) - require.True(math.IntEq(t, newNotBonded, oldNotBonded.SubRaw(int64(maxEntries)))) - - oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - - // unbonding should work again - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1)) - require.NoError(t, err) - - newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount - newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(1))) - require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(1))) + require.Equal(remainingTokens, delegation.Shares.RoundInt()) + require.Equal(remainingTokens, validator.BondedTokens()) } // // test undelegating self delegation from a validator pushing it below MinSelfDelegation // // shift it from the bonded to unbonding state and jailed -func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestUndelegateSelfDelegationBelowMinSelfDelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) + addrDels, addrVals := createValAddrs(1) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) validator.MinSelfDelegation = delTokens validator, issuedShares := validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + require.Equal(delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) - require.True(t, validator.IsBonded()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + keeper.SetValidatorByConsAddr(ctx, validator) + require.True(validator.IsBonded()) - selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) - - // add bonded tokens to pool for delegations - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + selfDelegation := stakingtypes.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) + keeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.True(t, validator.IsBonded()) - require.Equal(t, delTokens, issuedShares.RoundInt()) - - // add bonded tokens to pool for delegations - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + require.True(validator.IsBonded()) + require.Equal(delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + delegation := stakingtypes.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 6))) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(keeper.TokensFromConsensusPower(ctx, 6))) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 14), validator.Tokens) - require.Equal(t, types.Unbonding, validator.Status) - require.True(t, validator.Jailed) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(keeper.TokensFromConsensusPower(ctx, 14), validator.Tokens) + require.Equal(stakingtypes.Unbonding, validator.Status) + require.True(validator.Jailed) } -func TestUndelegateFromUnbondingValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) +func (s *KeeperTestSuite) TestUndelegateFromUnbondingValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() + delTokens := keeper.TokensFromConsensusPower(ctx, 10) - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) validator, issuedShares := validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + require.Equal(delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) - selfDelegation := types.NewDelegation(addrVals[0].Bytes(), addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) - - // add bonded tokens to pool for delegations - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + selfDelegation := stakingtypes.NewDelegation(addrVals[0].Bytes(), addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) + keeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + require.Equal(delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + delegation := stakingtypes.NewDelegation(addrDels[1], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) header := ctx.BlockHeader() blockHeight := int64(10) @@ -436,17 +313,19 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { // unbond the all self-delegation to put validator in unbonding state val0AccAddr := sdk.AccAddress(addrVals[0]) - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, blockHeight, validator.UnbondingHeight) - params := app.StakingKeeper.GetParams(ctx) - require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) blockHeight2 := int64(20) blockTime2 := time.Unix(444, 0).UTC() @@ -454,443 +333,406 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockTime(blockTime2) // unbond some of the other delegation's shares - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], math.LegacyNewDec(6)) - require.NoError(t, err) + _, err = keeper.Undelegate(ctx, addrDels[1], addrVals[0], math.LegacyNewDec(6)) + require.NoError(err) // retrieve the unbonding delegation - ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[1], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - require.True(t, ubd.Entries[0].Balance.Equal(sdk.NewInt(6))) - assert.Equal(t, blockHeight2, ubd.Entries[0].CreationHeight) - assert.True(t, blockTime2.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) + ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[1], addrVals[0]) + require.True(found) + require.Len(ubd.Entries, 1) + require.True(ubd.Entries[0].Balance.Equal(sdk.NewInt(6))) + require.Equal(blockHeight2, ubd.Entries[0].CreationHeight) + require.True(blockTime2.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) } -func TestUndelegateFromUnbondedValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) +func (s *KeeperTestSuite) TestUndelegateFromUnbondedValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) val0AccAddr := sdk.AccAddress(addrVals[0]) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) - - // add bonded tokens to pool for delegations - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) + keeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) - delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + require.Equal(delTokens, issuedShares.RoundInt()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) + delegation := stakingtypes.NewDelegation(addrDels[1], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) - params := app.StakingKeeper.GetParams(ctx) - require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(ctx.BlockHeight(), validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) // unbond the validator ctx = ctx.WithBlockTime(validator.UnbondingTime) - app.StakingKeeper.UnbondAllMatureValidators(ctx) + keeper.UnbondAllMatureValidators(ctx) // Make sure validator is still in state because there is still an outstanding delegation - validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, validator.Status, types.Unbonded) + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(validator.Status, stakingtypes.Unbonded) // unbond some of the other delegation's shares - unbondTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(unbondTokens)) - require.NoError(t, err) + unbondTokens := keeper.TokensFromConsensusPower(ctx, 6) + _, err = keeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(unbondTokens)) + require.NoError(err) // unbond rest of the other delegation's shares remainingTokens := delTokens.Sub(unbondTokens) - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(remainingTokens)) - require.NoError(t, err) + _, err = keeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(remainingTokens)) + require.NoError(err) // now validator should be deleted from state - validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.False(t, found, "%v", validator) + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.False(found, "%v", validator) } -func TestUnbondingAllDelegationFromValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) +func (s *KeeperTestSuite) TestUnbondingAllDelegationFromValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) + require.Equal(valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) + keeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - - // add bonded tokens to pool for delegations - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), delCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + require.Equal(delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) - delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + delegation := stakingtypes.NewDelegation(addrDels[1], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) // unbond all the remaining delegation - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(delTokens)) - require.NoError(t, err) + _, err = keeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDecFromInt(delTokens)) + require.NoError(err) // validator should still be in state and still be in unbonding state - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, validator.Status, types.Unbonding) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(validator.Status, stakingtypes.Unbonding) // unbond the validator ctx = ctx.WithBlockTime(validator.UnbondingTime) - app.StakingKeeper.UnbondAllMatureValidators(ctx) + keeper.UnbondAllMatureValidators(ctx) // validator should now be deleted from state - _, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.False(t, found) + _, found = keeper.GetValidator(ctx, addrVals[0]) + require.False(found) } // Make sure that that the retrieving the delegations doesn't affect the state -func TestGetRedelegationsFromSrcValidator(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestGetRedelegationsFromSrcValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + addrDels, addrVals := createValAddrs(2) - rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + rd := stakingtypes.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, time.Unix(0, 0), sdk.NewInt(5), math.LegacyNewDec(5)) // set and retrieve a record - app.StakingKeeper.SetRedelegation(ctx, rd) - resBond, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) + keeper.SetRedelegation(ctx, rd) + resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(found) // get the redelegations one time - redelegations := app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resBond) + redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resBond) // get the redelegations a second time, should be exactly the same - redelegations = app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resBond) + redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resBond) } // tests Get/Set/Remove/Has UnbondingDelegation -func TestRedelegation(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestRedelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + addrDels, addrVals := createValAddrs(2) - rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + rd := stakingtypes.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, time.Unix(0, 0).UTC(), sdk.NewInt(5), math.LegacyNewDec(5)) // test shouldn't have and redelegations - has := app.StakingKeeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) - require.False(t, has) + has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + require.False(has) // set and retrieve a record - app.StakingKeeper.SetRedelegation(ctx, rd) - resRed, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) + keeper.SetRedelegation(ctx, rd) + resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(found) - redelegations := app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resRed) + redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resRed) - redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resRed) + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resRed) - redelegations = app.StakingKeeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resRed) + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resRed) // check if has the redelegation - has = app.StakingKeeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) - require.True(t, has) + has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + require.True(has) // modify a records, save, and retrieve rd.Entries[0].SharesDst = math.LegacyNewDec(21) - app.StakingKeeper.SetRedelegation(ctx, rd) + keeper.SetRedelegation(ctx, rd) - resRed, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Equal(t, rd, resRed) + resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(found) + require.Equal(rd, resRed) - redelegations = app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resRed) + redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resRed) - redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) - require.Equal(t, 1, len(redelegations)) - require.Equal(t, redelegations[0], resRed) + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(1, len(redelegations)) + require.Equal(redelegations[0], resRed) // delete a record - app.StakingKeeper.RemoveRedelegation(ctx, rd) - _, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.False(t, found) + keeper.RemoveRedelegation(ctx, rd) + _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.False(found) - redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) - require.Equal(t, 0, len(redelegations)) + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(0, len(redelegations)) - redelegations = app.StakingKeeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) - require.Equal(t, 0, len(redelegations)) + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + require.Equal(0, len(redelegations)) } -func TestRedelegateToSameValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens)) +func (s *KeeperTestSuite) TestRedelegateToSameValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + _, addrVals := createValAddrs(1) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.True(t, validator.IsBonded()) + require.Equal(valTokens, issuedShares.RoundInt()) + + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.True(validator.IsBonded()) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) - _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], math.LegacyNewDec(5)) - require.Error(t, err) + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], math.LegacyNewDec(5)) + require.Error(err) } -func TestRedelegationMaxEntries(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 20) - startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) +func (s *KeeperTestSuite) TestRedelegationMaxEntries() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + _, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.Equal(valTokens, issuedShares.RoundInt()) + + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second validator - validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) + validator2 := teststaking.NewValidator(s.T(), addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) + require.Equal(valTokens, issuedShares.RoundInt()) - validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) - require.Equal(t, types.Bonded, validator2.Status) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator2 = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(stakingtypes.Bonded, validator2.Status) - maxEntries := app.StakingKeeper.MaxEntries(ctx) + maxEntries := keeper.MaxEntries(ctx) // redelegations should pass var completionTime time.Time for i := uint32(0); i < maxEntries; i++ { var err error - completionTime, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) - require.NoError(t, err) + completionTime, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) + require.NoError(err) } // an additional redelegation should fail due to max entries - _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) - require.Error(t, err) + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) + require.Error(err) // mature redelegations ctx = ctx.WithBlockTime(completionTime) - _, err = app.StakingKeeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) - require.NoError(t, err) + _, err = keeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) + require.NoError(err) // redelegation should work again - _, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) - require.NoError(t, err) + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], math.LegacyNewDec(1)) + require.NoError(err) } -func TestRedelegateSelfDelegation(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) +func (s *KeeperTestSuite) TestRedelegateSelfDelegation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) - startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) - - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) + require.Equal(valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0]) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second validator - validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) + validator2 := teststaking.NewValidator(s.T(), addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) - require.Equal(t, types.Bonded, validator2.Status) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator2 = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(stakingtypes.Bonded, validator2.Status) // create a second delegation to validator 1 - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.Equal(delTokens, issuedShares.RoundInt()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + delegation := stakingtypes.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) - _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDecFromInt(delTokens)) - require.NoError(t, err) + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDecFromInt(delTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 2) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, valTokens, validator.Tokens) - require.Equal(t, types.Unbonding, validator.Status) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(valTokens, validator.Tokens) + require.Equal(stakingtypes.Unbonding, validator.Status) } -func TestRedelegateFromUnbondingValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) +func (s *KeeperTestSuite) TestRedelegateFromUnbondingValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) - startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) - - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + keeper.DeleteValidatorByPowerIndex(ctx, validator) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + require.Equal(delTokens, issuedShares.RoundInt()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + delegation := stakingtypes.NewDelegation(addrDels[1], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) // create a second validator - validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) + validator2 := teststaking.NewValidator(s.T(), addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator2 = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator2, true) header := ctx.BlockHeader() blockHeight := int64(10) @@ -900,17 +742,19 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, blockHeight, validator.UnbondingHeight) - params := app.StakingKeeper.GetParams(ctx) - require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) // change the context header = ctx.BlockHeader() @@ -921,85 +765,83 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond some of the other delegation's shares - redelegateTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - _, err = app.StakingKeeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegateTokens)) - require.NoError(t, err) + redelegateTokens := keeper.TokensFromConsensusPower(ctx, 6) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + _, err = keeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegateTokens)) + require.NoError(err) // retrieve the unbonding delegation - ubd, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight) - assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) + ubd, found := keeper.GetRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1]) + require.True(found) + require.Len(ubd.Entries, 1) + require.Equal(blockHeight, ubd.Entries[0].CreationHeight) + require.True(blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) } -func TestRedelegateFromUnbondedValidator(t *testing.T) { - _, app, ctx := createTestInput(t) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) - - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) - startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) +func (s *KeeperTestSuite) TestRedelegateFromUnbondedValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // add bonded tokens to pool for delegations - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + addrDels, addrVals := createValAddrs(2) // create a validator with a self-delegation - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator := teststaking.NewValidator(s.T(), addrVals[0], PKs[0]) + keeper.SetValidatorByConsAddr(ctx, validator) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares := validator.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, selfDelegation) + selfDelegation := stakingtypes.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + keeper.DeleteValidatorByPowerIndex(ctx, validator) + delTokens := keeper.TokensFromConsensusPower(ctx, 10) validator, issuedShares = validator.AddTokensFromDel(delTokens) - require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) - app.StakingKeeper.SetDelegation(ctx, delegation) + require.Equal(delTokens, issuedShares.RoundInt()) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + delegation := stakingtypes.NewDelegation(addrDels[1], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) // create a second validator - validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) + validator2 := teststaking.NewValidator(s.T(), addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) - require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) - require.Equal(t, types.Bonded, validator2.Status) + require.Equal(valTokens, issuedShares.RoundInt()) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validator2 = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(stakingtypes.Bonded, validator2.Status) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) - require.NoError(t, err) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) + require.NoError(err) // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 1) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) - params := app.StakingKeeper.GetParams(ctx) - require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(found) + require.Equal(ctx.BlockHeight(), validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) // unbond the validator - app.StakingKeeper.UnbondingToUnbonded(ctx, validator) + keeper.UnbondingToUnbonded(ctx, validator) // redelegate some of the delegation's shares - redelegationTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - _, err = app.StakingKeeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegationTokens)) - require.NoError(t, err) + redelegationTokens := keeper.TokensFromConsensusPower(ctx, 6) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + _, err = keeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegationTokens)) + require.NoError(err) // no red should have been found - red, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.False(t, found, "%v", red) + red, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.False(found, "%v", red) } diff --git a/x/staking/keeper/grpc_query_test.go b/x/staking/keeper/grpc_query_test.go index 6217fa0c8202b..5f43dc33a3407 100644 --- a/x/staking/keeper/grpc_query_test.go +++ b/x/staking/keeper/grpc_query_test.go @@ -4,89 +4,17 @@ import ( gocontext "context" "fmt" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func (suite *KeeperTestSuite) TestGRPCQueryValidators() { - queryClient, vals := suite.queryClient, suite.vals - var req *types.QueryValidatorsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - numVals int - hasNext bool - }{ - { - "empty request", - func() { - req = &types.QueryValidatorsRequest{} - }, - true, - - len(vals) + 1, // +1 validator from genesis state - false, - }, - { - "empty status returns all the validators", - func() { - req = &types.QueryValidatorsRequest{Status: ""} - }, - true, - len(vals) + 1, // +1 validator from genesis state - false, - }, - { - "invalid request", - func() { - req = &types.QueryValidatorsRequest{Status: "test"} - }, - false, - 0, - false, - }, - { - "valid request", - func() { - req = &types.QueryValidatorsRequest{ - Status: types.Bonded.String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - 1, - true, - }, - } - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - valsResp, err := queryClient.Validators(gocontext.Background(), req) - if tc.expPass { - suite.NoError(err) - suite.NotNil(valsResp) - suite.Equal(tc.numVals, len(valsResp.Validators)) - suite.Equal(uint64(len(vals))+1, valsResp.Pagination.Total) // +1 validator from genesis state +func (s *KeeperTestSuite) TestGRPCQueryValidator() { + ctx, keeper, queryClient := s.ctx, s.stakingKeeper, s.queryClient + require := s.Require() - if tc.hasNext { - suite.NotNil(valsResp.Pagination.NextKey) - } else { - suite.Nil(valsResp.Pagination.NextKey) - } - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryValidator() { - app, ctx, queryClient, vals := suite.app, suite.ctx, suite.queryClient, suite.vals - validator, found := app.StakingKeeper.GetValidator(ctx, vals[0].GetOperator()) - suite.True(found) + validator := teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) + keeper.SetValidator(ctx, validator) var req *types.QueryValidatorRequest testCases := []struct { msg string @@ -103,678 +31,22 @@ func (suite *KeeperTestSuite) TestGRPCQueryValidator() { { "valid request", func() { - req = &types.QueryValidatorRequest{ValidatorAddr: vals[0].OperatorAddress} + req = &types.QueryValidatorRequest{ValidatorAddr: validator.OperatorAddress} }, true, }, } for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + s.Run(fmt.Sprintf("Case %s", tc.msg), func() { tc.malleate() res, err := queryClient.Validator(gocontext.Background(), req) if tc.expPass { - suite.NoError(err) - suite.True(validator.Equal(&res.Validator)) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryDelegatorValidators() { - app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs - params := app.StakingKeeper.GetParams(ctx) - delValidators := app.StakingKeeper.GetDelegatorValidators(ctx, addrs[0], params.MaxValidators) - var req *types.QueryDelegatorValidatorsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryDelegatorValidatorsRequest{} - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryDelegatorValidatorsRequest{ - DelegatorAddr: addrs[0].String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.DelegatorValidators(gocontext.Background(), req) - if tc.expPass { - suite.NoError(err) - suite.Equal(1, len(res.Validators)) - suite.NotNil(res.Pagination.NextKey) - suite.Equal(uint64(len(delValidators)), res.Pagination.Total) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryDelegatorValidator() { - queryClient, addrs, vals := suite.queryClient, suite.addrs, suite.vals - addr := addrs[1] - addrVal, addrVal1 := vals[0].OperatorAddress, vals[1].OperatorAddress - var req *types.QueryDelegatorValidatorRequest - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryDelegatorValidatorRequest{} - }, - false, - }, - { - "invalid delegator, validator pair", - func() { - req = &types.QueryDelegatorValidatorRequest{ - DelegatorAddr: addr.String(), - ValidatorAddr: addrVal, - } - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryDelegatorValidatorRequest{ - DelegatorAddr: addr.String(), - ValidatorAddr: addrVal1, - } - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.DelegatorValidator(gocontext.Background(), req) - if tc.expPass { - suite.NoError(err) - suite.Equal(addrVal1, res.Validator.OperatorAddress) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryDelegation() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc, addrAcc1 := addrs[0], addrs[1] - addrVal := vals[0].OperatorAddress - valAddr, err := sdk.ValAddressFromBech32(addrVal) - suite.NoError(err) - delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) - suite.True(found) - var req *types.QueryDelegationRequest - - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryDelegationRequest{} - }, - false, - }, - { - "invalid validator, delegator pair", - func() { - req = &types.QueryDelegationRequest{ - DelegatorAddr: addrAcc1.String(), - ValidatorAddr: addrVal, - } - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryDelegationRequest{DelegatorAddr: addrAcc.String(), ValidatorAddr: addrVal} - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.Delegation(gocontext.Background(), req) - if tc.expPass { - suite.Equal(delegation.ValidatorAddress, res.DelegationResponse.Delegation.ValidatorAddress) - suite.Equal(delegation.DelegatorAddress, res.DelegationResponse.Delegation.DelegatorAddress) - suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponse.Balance) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryDelegatorDelegations() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc := addrs[0] - addrVal1 := vals[0].OperatorAddress - valAddr, err := sdk.ValAddressFromBech32(addrVal1) - suite.NoError(err) - delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) - suite.True(found) - var req *types.QueryDelegatorDelegationsRequest - - testCases := []struct { - msg string - malleate func() - onSuccess func(suite *KeeperTestSuite, response *types.QueryDelegatorDelegationsResponse) - expErr bool - }{ - { - "empty request", - func() { - req = &types.QueryDelegatorDelegationsRequest{} - }, - func(suite *KeeperTestSuite, response *types.QueryDelegatorDelegationsResponse) {}, - true, - }, - { - "valid request with no delegations", - func() { - req = &types.QueryDelegatorDelegationsRequest{DelegatorAddr: addrs[4].String()} - }, - func(suite *KeeperTestSuite, response *types.QueryDelegatorDelegationsResponse) { - suite.Equal(uint64(0), response.Pagination.Total) - suite.Len(response.DelegationResponses, 0) - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryDelegatorDelegationsRequest{ - DelegatorAddr: addrAcc.String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - func(suite *KeeperTestSuite, response *types.QueryDelegatorDelegationsResponse) { - suite.Equal(uint64(2), response.Pagination.Total) - suite.Len(response.DelegationResponses, 1) - suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), response.DelegationResponses[0].Balance) - }, - false, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.DelegatorDelegations(gocontext.Background(), req) - if tc.expErr { - suite.Error(err) - } else { - suite.NoError(err) - tc.onSuccess(suite, res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryValidatorDelegations() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc := addrs[0] - addrVal1 := vals[1].OperatorAddress - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - addrVal2 := valAddrs[4] - valAddr, err := sdk.ValAddressFromBech32(addrVal1) - suite.NoError(err) - delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) - suite.True(found) - - var req *types.QueryValidatorDelegationsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - expErr bool - }{ - { - "empty request", - func() { - req = &types.QueryValidatorDelegationsRequest{} - }, - false, - true, - }, - { - "invalid validator delegator pair", - func() { - req = &types.QueryValidatorDelegationsRequest{ValidatorAddr: addrVal2.String()} - }, - false, - false, - }, - { - "valid request", - func() { - req = &types.QueryValidatorDelegationsRequest{ - ValidatorAddr: addrVal1, - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - false, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.ValidatorDelegations(gocontext.Background(), req) - if tc.expPass && !tc.expErr { - suite.NoError(err) - suite.Len(res.DelegationResponses, 1) - suite.NotNil(res.Pagination.NextKey) - suite.Equal(uint64(2), res.Pagination.Total) - suite.Equal(addrVal1, res.DelegationResponses[0].Delegation.ValidatorAddress) - suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponses[0].Balance) - } else if !tc.expPass && !tc.expErr { - suite.NoError(err) - suite.Nil(res.DelegationResponses) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryUnbondingDelegation() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc2 := addrs[1] - addrVal2 := vals[1].OperatorAddress - - unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) - valAddr, err1 := sdk.ValAddressFromBech32(addrVal2) - suite.NoError(err1) - _, err := app.StakingKeeper.Undelegate(ctx, addrAcc2, valAddr, sdk.NewDecFromInt(unbondingTokens)) - suite.NoError(err) - - unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc2, valAddr) - suite.True(found) - var req *types.QueryUnbondingDelegationRequest - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryUnbondingDelegationRequest{} - }, - false, - }, - { - "invalid request", - func() { - req = &types.QueryUnbondingDelegationRequest{} - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryUnbondingDelegationRequest{ - DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal2, - } - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.UnbondingDelegation(gocontext.Background(), req) - if tc.expPass { - suite.NotNil(res) - suite.Equal(unbond, res.Unbond) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryDelegatorUnbondingDelegations() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc, addrAcc1 := addrs[0], addrs[1] - addrVal, addrVal2 := vals[0].OperatorAddress, vals[1].OperatorAddress - - unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) - valAddr1, err1 := sdk.ValAddressFromBech32(addrVal) - suite.NoError(err1) - _, err := app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr1, sdk.NewDecFromInt(unbondingTokens)) - suite.NoError(err) - valAddr2, err1 := sdk.ValAddressFromBech32(addrVal2) - suite.NoError(err1) - _, err = app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr2, sdk.NewDecFromInt(unbondingTokens)) - suite.NoError(err) - - unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc, valAddr1) - suite.True(found) - var req *types.QueryDelegatorUnbondingDelegationsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - expErr bool - }{ - { - "empty request", - func() { - req = &types.QueryDelegatorUnbondingDelegationsRequest{} - }, - false, - true, - }, - { - "invalid request", - func() { - req = &types.QueryDelegatorUnbondingDelegationsRequest{DelegatorAddr: addrAcc1.String()} - }, - false, - false, - }, - { - "valid request", - func() { - req = &types.QueryDelegatorUnbondingDelegationsRequest{ - DelegatorAddr: addrAcc.String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - false, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.DelegatorUnbondingDelegations(gocontext.Background(), req) - if tc.expPass && !tc.expErr { - suite.NoError(err) - suite.NotNil(res.Pagination.NextKey) - suite.Equal(uint64(2), res.Pagination.Total) - suite.Len(res.UnbondingResponses, 1) - suite.Equal(unbond, res.UnbondingResponses[0]) - } else if !tc.expPass && !tc.expErr { - suite.NoError(err) - suite.Nil(res.UnbondingResponses) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryPoolParameters() { - app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient - bondDenom := sdk.DefaultBondDenom - - // Query pool - res, err := queryClient.Pool(gocontext.Background(), &types.QueryPoolRequest{}) - suite.NoError(err) - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - suite.Equal(app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, res.Pool.NotBondedTokens) - suite.Equal(app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, res.Pool.BondedTokens) - - // Query Params - resp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) - suite.NoError(err) - suite.Equal(app.StakingKeeper.GetParams(ctx), resp.Params) -} - -func (suite *KeeperTestSuite) TestGRPCQueryHistoricalInfo() { - app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient - - hi, found := app.StakingKeeper.GetHistoricalInfo(ctx, 5) - suite.True(found) - - var req *types.QueryHistoricalInfoRequest - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryHistoricalInfoRequest{} - }, - false, - }, - { - "invalid request with negative height", - func() { - req = &types.QueryHistoricalInfoRequest{Height: -1} - }, - false, - }, - { - "valid request with old height", - func() { - req = &types.QueryHistoricalInfoRequest{Height: 4} - }, - false, - }, - { - "valid request with current height", - func() { - req = &types.QueryHistoricalInfoRequest{Height: 5} - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.HistoricalInfo(gocontext.Background(), req) - if tc.expPass { - suite.NoError(err) - suite.NotNil(res) - suite.True(hi.Equal(res.Hist)) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryRedelegations() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - - addrAcc, addrAcc1 := addrs[0], addrs[1] - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - val1, val2, val3, val4 := vals[0], vals[1], valAddrs[3], valAddrs[4] - delAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) - _, err := app.StakingKeeper.Delegate(ctx, addrAcc1, delAmount, types.Unbonded, val1, true) - suite.NoError(err) - applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) - - rdAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) - _, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator(), sdk.NewDecFromInt(rdAmount)) - suite.NoError(err) - applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) - - redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator()) - suite.True(found) - - var req *types.QueryRedelegationsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - expErr bool - }{ - { - "request redelegations for non existent addr", - func() { - req = &types.QueryRedelegationsRequest{DelegatorAddr: addrAcc.String()} - }, - false, - false, - }, - { - "request redelegations with non existent pairs", - func() { - req = &types.QueryRedelegationsRequest{ - DelegatorAddr: addrAcc.String(), SrcValidatorAddr: val3.String(), - DstValidatorAddr: val4.String(), - } - }, - false, - true, - }, - { - "request redelegations with delegatoraddr, sourceValAddr, destValAddr", - func() { - req = &types.QueryRedelegationsRequest{ - DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, - DstValidatorAddr: val2.OperatorAddress, Pagination: &query.PageRequest{}, - } - }, - true, - false, - }, - { - "request redelegations with delegatoraddr and sourceValAddr", - func() { - req = &types.QueryRedelegationsRequest{ - DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, - Pagination: &query.PageRequest{}, - } - }, - true, - false, - }, - { - "query redelegations with sourceValAddr only", - func() { - req = &types.QueryRedelegationsRequest{ - SrcValidatorAddr: val1.GetOperator().String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - false, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.Redelegations(gocontext.Background(), req) - if tc.expPass && !tc.expErr { - suite.NoError(err) - suite.Len(res.RedelegationResponses, len(redel.Entries)) - suite.Equal(redel.DelegatorAddress, res.RedelegationResponses[0].Redelegation.DelegatorAddress) - suite.Equal(redel.ValidatorSrcAddress, res.RedelegationResponses[0].Redelegation.ValidatorSrcAddress) - suite.Equal(redel.ValidatorDstAddress, res.RedelegationResponses[0].Redelegation.ValidatorDstAddress) - suite.Len(redel.Entries, len(res.RedelegationResponses[0].Entries)) - } else if !tc.expPass && !tc.expErr { - suite.NoError(err) - suite.Nil(res.RedelegationResponses) - } else { - suite.Error(err) - suite.Nil(res) - } - }) - } -} - -func (suite *KeeperTestSuite) TestGRPCQueryValidatorUnbondingDelegations() { - app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals - addrAcc1, _ := addrs[0], addrs[1] - val1 := vals[0] - - // undelegate - undelAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 2) - _, err := app.StakingKeeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), sdk.NewDecFromInt(undelAmount)) - suite.NoError(err) - applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) - - var req *types.QueryValidatorUnbondingDelegationsRequest - testCases := []struct { - msg string - malleate func() - expPass bool - }{ - { - "empty request", - func() { - req = &types.QueryValidatorUnbondingDelegationsRequest{} - }, - false, - }, - { - "valid request", - func() { - req = &types.QueryValidatorUnbondingDelegationsRequest{ - ValidatorAddr: val1.GetOperator().String(), - Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, - } - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - tc.malleate() - res, err := queryClient.ValidatorUnbondingDelegations(gocontext.Background(), req) - if tc.expPass { - suite.NoError(err) - suite.Equal(uint64(1), res.Pagination.Total) - suite.Equal(1, len(res.UnbondingResponses)) - suite.Equal(res.UnbondingResponses[0].ValidatorAddress, val1.OperatorAddress) + require.NoError(err) + require.True(validator.Equal(&res.Validator)) } else { - suite.Error(err) - suite.Nil(res) + require.Error(err) + require.Nil(res) } }) } diff --git a/x/staking/keeper/historical_info_test.go b/x/staking/keeper/historical_info_test.go index c1a5f35252a05..8756817fea531 100644 --- a/x/staking/keeper/historical_info_test.go +++ b/x/staking/keeper/historical_info_test.go @@ -1,67 +1,61 @@ package keeper_test import ( - "testing" - "cosmossdk.io/math" - "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - "github.com/cosmos/cosmos-sdk/x/staking/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // IsValSetSorted reports whether valset is sorted. -func IsValSetSorted(data []types.Validator, powerReduction math.Int) bool { +func IsValSetSorted(data []stakingtypes.Validator, powerReduction math.Int) bool { n := len(data) for i := n - 1; i > 0; i-- { - if types.ValidatorsByVotingPower(data).Less(i, i-1, powerReduction) { + if stakingtypes.ValidatorsByVotingPower(data).Less(i, i-1, powerReduction) { return false } } return true } -func TestHistoricalInfo(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestHistoricalInfo() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + _, addrVals := createValAddrs(50) - validators := make([]types.Validator, len(addrVals)) + validators := make([]stakingtypes.Validator, len(addrVals)) for i, valAddr := range addrVals { - validators[i] = teststaking.NewValidator(t, valAddr, PKs[i]) + validators[i] = teststaking.NewValidator(s.T(), valAddr, PKs[i]) } - hi := types.NewHistoricalInfo(ctx.BlockHeader(), validators, app.StakingKeeper.PowerReduction(ctx)) - app.StakingKeeper.SetHistoricalInfo(ctx, 2, &hi) + hi := stakingtypes.NewHistoricalInfo(ctx.BlockHeader(), validators, keeper.PowerReduction(ctx)) + keeper.SetHistoricalInfo(ctx, 2, &hi) - recv, found := app.StakingKeeper.GetHistoricalInfo(ctx, 2) - require.True(t, found, "HistoricalInfo not found after set") - require.Equal(t, hi, recv, "HistoricalInfo not equal") - require.True(t, IsValSetSorted(recv.Valset, app.StakingKeeper.PowerReduction(ctx)), "HistoricalInfo validators is not sorted") + recv, found := keeper.GetHistoricalInfo(ctx, 2) + require.True(found, "HistoricalInfo not found after set") + require.Equal(hi, recv, "HistoricalInfo not equal") + require.True(IsValSetSorted(recv.Valset, keeper.PowerReduction(ctx)), "HistoricalInfo validators is not sorted") - app.StakingKeeper.DeleteHistoricalInfo(ctx, 2) + keeper.DeleteHistoricalInfo(ctx, 2) - recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 2) - require.False(t, found, "HistoricalInfo found after delete") - require.Equal(t, types.HistoricalInfo{}, recv, "HistoricalInfo is not empty") + recv, found = keeper.GetHistoricalInfo(ctx, 2) + require.False(found, "HistoricalInfo found after delete") + require.Equal(stakingtypes.HistoricalInfo{}, recv, "HistoricalInfo is not empty") } -func TestTrackHistoricalInfo(t *testing.T) { - _, app, ctx := createTestInput(t) +func (s *KeeperTestSuite) TestTrackHistoricalInfo() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + _, addrVals := createValAddrs(50) // set historical entries in params to 5 - params := types.DefaultParams() + params := stakingtypes.DefaultParams() params.HistoricalEntries = 5 - app.StakingKeeper.SetParams(ctx, params) + keeper.SetParams(ctx, params) // set historical info at 5, 4 which should be pruned // and check that it has been stored @@ -73,39 +67,35 @@ func TestTrackHistoricalInfo(t *testing.T) { ChainID: "HelloChain", Height: 5, } - valSet := []types.Validator{ - teststaking.NewValidator(t, addrVals[0], PKs[0]), - teststaking.NewValidator(t, addrVals[1], PKs[1]), + valSet := []stakingtypes.Validator{ + teststaking.NewValidator(s.T(), addrVals[0], PKs[0]), + teststaking.NewValidator(s.T(), addrVals[1], PKs[1]), } - hi4 := types.NewHistoricalInfo(h4, valSet, app.StakingKeeper.PowerReduction(ctx)) - hi5 := types.NewHistoricalInfo(h5, valSet, app.StakingKeeper.PowerReduction(ctx)) - app.StakingKeeper.SetHistoricalInfo(ctx, 4, &hi4) - app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi5) - recv, found := app.StakingKeeper.GetHistoricalInfo(ctx, 4) - require.True(t, found) - require.Equal(t, hi4, recv) - recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 5) - require.True(t, found) - require.Equal(t, hi5, recv) - - // genesis validator - genesisVals := app.StakingKeeper.GetAllValidators(ctx) - require.Len(t, genesisVals, 1) + hi4 := stakingtypes.NewHistoricalInfo(h4, valSet, keeper.PowerReduction(ctx)) + hi5 := stakingtypes.NewHistoricalInfo(h5, valSet, keeper.PowerReduction(ctx)) + keeper.SetHistoricalInfo(ctx, 4, &hi4) + keeper.SetHistoricalInfo(ctx, 5, &hi5) + recv, found := keeper.GetHistoricalInfo(ctx, 4) + require.True(found) + require.Equal(hi4, recv) + recv, found = keeper.GetHistoricalInfo(ctx, 5) + require.True(found) + require.Equal(hi5, recv) // Set bonded validators in keeper - val1 := teststaking.NewValidator(t, addrVals[2], PKs[2]) - val1.Status = types.Bonded // when not bonded, consensus power is Zero - val1.Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - app.StakingKeeper.SetValidator(ctx, val1) - app.StakingKeeper.SetLastValidatorPower(ctx, val1.GetOperator(), 10) - val2 := teststaking.NewValidator(t, addrVals[3], PKs[3]) - val1.Status = types.Bonded - val2.Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 80) - app.StakingKeeper.SetValidator(ctx, val2) - app.StakingKeeper.SetLastValidatorPower(ctx, val2.GetOperator(), 80) - - vals := []types.Validator{val1, genesisVals[0], val2} - require.True(t, IsValSetSorted(vals, app.StakingKeeper.PowerReduction(ctx))) + val1 := teststaking.NewValidator(s.T(), addrVals[2], PKs[2]) + val1.Status = stakingtypes.Bonded // when not bonded, consensus power is Zero + val1.Tokens = keeper.TokensFromConsensusPower(ctx, 10) + keeper.SetValidator(ctx, val1) + keeper.SetLastValidatorPower(ctx, val1.GetOperator(), 10) + val2 := teststaking.NewValidator(s.T(), addrVals[3], PKs[3]) + val1.Status = stakingtypes.Bonded + val2.Tokens = keeper.TokensFromConsensusPower(ctx, 80) + keeper.SetValidator(ctx, val2) + keeper.SetLastValidatorPower(ctx, val2.GetOperator(), 80) + + vals := []stakingtypes.Validator{val1, val2} + require.True(IsValSetSorted(vals, keeper.PowerReduction(ctx))) // Set Header for BeginBlock context header := tmproto.Header{ @@ -114,55 +104,51 @@ func TestTrackHistoricalInfo(t *testing.T) { } ctx = ctx.WithBlockHeader(header) - app.StakingKeeper.TrackHistoricalInfo(ctx) + keeper.TrackHistoricalInfo(ctx) // Check HistoricalInfo at height 10 is persisted - expected := types.HistoricalInfo{ + expected := stakingtypes.HistoricalInfo{ Header: header, Valset: vals, } - recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 10) - require.True(t, found, "GetHistoricalInfo failed after BeginBlock") - require.Equal(t, expected, recv, "GetHistoricalInfo returned unexpected result") + recv, found = keeper.GetHistoricalInfo(ctx, 10) + require.True(found, "GetHistoricalInfo failed after BeginBlock") + require.Equal(expected, recv, "GetHistoricalInfo returned unexpected result") // Check HistoricalInfo at height 5, 4 is pruned - recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 4) - require.False(t, found, "GetHistoricalInfo did not prune earlier height") - require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune") - recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 5) - require.False(t, found, "GetHistoricalInfo did not prune first prune height") - require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune") + recv, found = keeper.GetHistoricalInfo(ctx, 4) + require.False(found, "GetHistoricalInfo did not prune earlier height") + require.Equal(stakingtypes.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune") + recv, found = keeper.GetHistoricalInfo(ctx, 5) + require.False(found, "GetHistoricalInfo did not prune first prune height") + require.Equal(stakingtypes.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune") } -func TestGetAllHistoricalInfo(t *testing.T) { - _, app, ctx := createTestInput(t) - // clear historical info - infos := app.StakingKeeper.GetAllHistoricalInfo(ctx) - require.Len(t, infos, 1) - app.StakingKeeper.DeleteHistoricalInfo(ctx, infos[0].Header.Height) +func (s *KeeperTestSuite) TestGetAllHistoricalInfo() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) - addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels) + _, addrVals := createValAddrs(50) - valSet := []types.Validator{ - teststaking.NewValidator(t, addrVals[0], PKs[0]), - teststaking.NewValidator(t, addrVals[1], PKs[1]), + valSet := []stakingtypes.Validator{ + teststaking.NewValidator(s.T(), addrVals[0], PKs[0]), + teststaking.NewValidator(s.T(), addrVals[1], PKs[1]), } header1 := tmproto.Header{ChainID: "HelloChain", Height: 10} header2 := tmproto.Header{ChainID: "HelloChain", Height: 11} header3 := tmproto.Header{ChainID: "HelloChain", Height: 12} - hist1 := types.HistoricalInfo{Header: header1, Valset: valSet} - hist2 := types.HistoricalInfo{Header: header2, Valset: valSet} - hist3 := types.HistoricalInfo{Header: header3, Valset: valSet} + hist1 := stakingtypes.HistoricalInfo{Header: header1, Valset: valSet} + hist2 := stakingtypes.HistoricalInfo{Header: header2, Valset: valSet} + hist3 := stakingtypes.HistoricalInfo{Header: header3, Valset: valSet} - expHistInfos := []types.HistoricalInfo{hist1, hist2, hist3} + expHistInfos := []stakingtypes.HistoricalInfo{hist1, hist2, hist3} for i, hi := range expHistInfos { - app.StakingKeeper.SetHistoricalInfo(ctx, int64(10+i), &hi) + keeper.SetHistoricalInfo(ctx, int64(10+i), &hi) } - infos = app.StakingKeeper.GetAllHistoricalInfo(ctx) - require.Equal(t, expHistInfos, infos) + infos := keeper.GetAllHistoricalInfo(ctx) + require.Equal(expHistInfos, infos) } diff --git a/x/staking/keeper/keeper_test.go b/x/staking/keeper/keeper_test.go index 610acf4cb0301..09509d289b1ee 100644 --- a/x/staking/keeper/keeper_test.go +++ b/x/staking/keeper/keeper_test.go @@ -3,72 +3,95 @@ package keeper_test import ( "testing" - "github.com/stretchr/testify/require" + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/testutil" +) + +var ( + bondedAcc = authtypes.NewEmptyModuleAccount(stakingtypes.BondedPoolName) + notBondedAcc = authtypes.NewEmptyModuleAccount(stakingtypes.NotBondedPoolName) + PKs = simtestutil.CreateTestPubKeys(500) ) type KeeperTestSuite struct { suite.Suite - app *simapp.SimApp - ctx sdk.Context - addrs []sdk.AccAddress - vals []types.Validator - queryClient types.QueryClient - msgServer types.MsgServer + ctx sdk.Context + stakingKeeper *stakingkeeper.Keeper + bankKeeper *stakingtestutil.MockBankKeeper + accountKeeper *stakingtestutil.MockAccountKeeper + queryClient stakingtypes.QueryClient + msgServer stakingtypes.MsgServer } -func (suite *KeeperTestSuite) SetupTest() { - app := simapp.Setup(suite.T(), false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - querier := keeper.Querier{Keeper: app.StakingKeeper} - - queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, querier) - queryClient := types.NewQueryClient(queryHelper) - - suite.msgServer = keeper.NewMsgServerImpl(app.StakingKeeper) - - addrs, _, validators := createValidators(suite.T(), ctx, app, []int64{9, 8, 7}) - header := tmproto.Header{ - ChainID: "HelloChain", - Height: 5, - } - - // sort a copy of the validators, so that original validators does not - // have its order changed - sortedVals := make([]types.Validator, len(validators)) - copy(sortedVals, validators) - hi := types.NewHistoricalInfo(header, sortedVals, app.StakingKeeper.PowerReduction(ctx)) - app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi) - - suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals = app, ctx, queryClient, addrs, validators +func (s *KeeperTestSuite) SetupTest() { + key := sdk.NewKVStoreKey(stakingtypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(s.T(), key, sdk.NewTransientStoreKey("transient_test")) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()}) + encCfg := moduletestutil.MakeTestEncodingConfig() + + ctrl := gomock.NewController(s.T()) + accountKeeper := stakingtestutil.NewMockAccountKeeper(ctrl) + accountKeeper.EXPECT().GetModuleAddress(stakingtypes.BondedPoolName).Return(bondedAcc.GetAddress()) + accountKeeper.EXPECT().GetModuleAddress(stakingtypes.NotBondedPoolName).Return(notBondedAcc.GetAddress()) + bankKeeper := stakingtestutil.NewMockBankKeeper(ctrl) + + keeper := stakingkeeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + keeper.SetParams(ctx, stakingtypes.DefaultParams()) + + s.ctx = ctx + s.stakingKeeper = keeper + s.bankKeeper = bankKeeper + s.accountKeeper = accountKeeper + + stakingtypes.RegisterInterfaces(encCfg.InterfaceRegistry) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry) + stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: keeper}) + s.queryClient = stakingtypes.NewQueryClient(queryHelper) + s.msgServer = stakingkeeper.NewMsgServerImpl(keeper) } -func TestParams(t *testing.T) { - app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) +func (s *KeeperTestSuite) TestParams() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - expParams := types.DefaultParams() + expParams := stakingtypes.DefaultParams() + expParams.MaxValidators = 555 + expParams.MaxEntries = 111 + keeper.SetParams(ctx, expParams) + resParams := keeper.GetParams(ctx) + require.True(expParams.Equal(resParams)) +} - // check that the empty keeper loads the default - resParams := app.StakingKeeper.GetParams(ctx) - require.True(t, expParams.Equal(resParams)) +func (s *KeeperTestSuite) TestLastTotalPower() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // modify a params, save, and retrieve - expParams.MaxValidators = 777 - app.StakingKeeper.SetParams(ctx, expParams) - resParams = app.StakingKeeper.GetParams(ctx) - require.True(t, expParams.Equal(resParams)) + expTotalPower := math.NewInt(10 ^ 9) + keeper.SetLastTotalPower(ctx, expTotalPower) + resTotalPower := keeper.GetLastTotalPower(ctx) + require.True(expTotalPower.Equal(resTotalPower)) } func TestKeeperTestSuite(t *testing.T) { diff --git a/x/staking/keeper/msg_server_test.go b/x/staking/keeper/msg_server_test.go index 0f870285598bc..87a7dff909364 100644 --- a/x/staking/keeper/msg_server_test.go +++ b/x/staking/keeper/msg_server_test.go @@ -2,189 +2,49 @@ package keeper_test import ( "testing" - "time" "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func TestCancelUnbondingDelegation(t *testing.T) { - // setup the app - app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - msgServer := keeper.NewMsgServerImpl(app.StakingKeeper) - bondDenom := app.StakingKeeper.BondDenom(ctx) - - // set the not bonded pool module account - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 5) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)))) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - moduleBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, sdk.NewInt64Coin(bondDenom, startTokens.Int64()), moduleBalance) - - // accounts - delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000)) - validators := app.StakingKeeper.GetValidators(ctx, 10) - require.Equal(t, len(validators), 1) - - validatorAddr, err := sdk.ValAddressFromBech32(validators[0].OperatorAddress) - require.NoError(t, err) - delegatorAddr := delAddrs[0] - - // setting the ubd entry - unbondingAmount := sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 5) - ubd := types.NewUnbondingDelegation( - delegatorAddr, validatorAddr, 10, - ctx.BlockTime().Add(time.Minute*10), - unbondingAmount.Amount, - ) - - // set and retrieve a record - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - resUnbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) - require.True(t, found) - require.Equal(t, ubd, resUnbond) - - testCases := []struct { - Name string - ExceptErr bool - req types.MsgCancelUnbondingDelegation - }{ - { - Name: "invalid height", - ExceptErr: true, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(4)), - CreationHeight: 0, - }, - }, - { - Name: "invalid coin", - ExceptErr: true, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: sdk.NewCoin("dump_coin", sdk.NewInt(4)), - CreationHeight: 0, - }, - }, - { - Name: "validator not exists", - ExceptErr: true, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: sdk.ValAddress(sdk.AccAddress("asdsad")).String(), - Amount: unbondingAmount, - CreationHeight: 0, - }, - }, - { - Name: "invalid delegator address", - ExceptErr: true, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: "invalid_delegator_addrtess", - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: unbondingAmount, - CreationHeight: 0, - }, - }, - { - Name: "invalid amount", - ExceptErr: true, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: unbondingAmount.Add(sdk.NewInt64Coin(bondDenom, 10)), - CreationHeight: 10, - }, - }, - { - Name: "success", - ExceptErr: false, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1)), - CreationHeight: 10, - }, - }, - { - Name: "success", - ExceptErr: false, - req: types.MsgCancelUnbondingDelegation{ - DelegatorAddress: resUnbond.DelegatorAddress, - ValidatorAddress: resUnbond.ValidatorAddress, - Amount: unbondingAmount.Sub(unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1))), - CreationHeight: 10, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.Name, func(t *testing.T) { - _, err := msgServer.CancelUnbondingDelegation(ctx, &testCase.req) - if testCase.ExceptErr { - require.Error(t, err) - } else { - require.NoError(t, err) - balanceForNotBondedPool := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(notBondedPool.GetAddress()), bondDenom) - require.Equal(t, balanceForNotBondedPool, moduleBalance.Sub(testCase.req.Amount)) - moduleBalance = moduleBalance.Sub(testCase.req.Amount) - } - }) - } -} - -func TestMsgUpdateParams(t *testing.T) { - app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - msgServer := keeper.NewMsgServerImpl(app.StakingKeeper) +func (s *KeeperTestSuite) TestMsgUpdateParams() { + ctx, keeper, msgServer := s.ctx, s.stakingKeeper, s.msgServer + require := s.Require() testCases := []struct { name string - input *types.MsgUpdateParams + input *stakingtypes.MsgUpdateParams expErr bool expErrMsg string }{ { name: "valid params", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.DefaultParams(), + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.DefaultParams(), }, expErr: false, }, { name: "invalid authority", - input: &types.MsgUpdateParams{ + input: &stakingtypes.MsgUpdateParams{ Authority: "invalid", - Params: types.DefaultParams(), + Params: stakingtypes.DefaultParams(), }, expErr: true, expErrMsg: "invalid authority", }, { name: "negative commission rate", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.Params{ + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.Params{ MinCommissionRate: math.LegacyNewDec(-10), - UnbondingTime: types.DefaultUnbondingTime, - MaxValidators: types.DefaultMaxValidators, - MaxEntries: types.DefaultMaxEntries, - HistoricalEntries: types.DefaultHistoricalEntries, - BondDenom: types.BondStatusBonded, + UnbondingTime: stakingtypes.DefaultUnbondingTime, + MaxValidators: stakingtypes.DefaultMaxValidators, + MaxEntries: stakingtypes.DefaultMaxEntries, + HistoricalEntries: stakingtypes.DefaultHistoricalEntries, + BondDenom: stakingtypes.BondStatusBonded, }, }, expErr: true, @@ -192,15 +52,15 @@ func TestMsgUpdateParams(t *testing.T) { }, { name: "commission rate cannot be bigger than 100", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.Params{ + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.Params{ MinCommissionRate: math.LegacyNewDec(2), - UnbondingTime: types.DefaultUnbondingTime, - MaxValidators: types.DefaultMaxValidators, - MaxEntries: types.DefaultMaxEntries, - HistoricalEntries: types.DefaultHistoricalEntries, - BondDenom: types.BondStatusBonded, + UnbondingTime: stakingtypes.DefaultUnbondingTime, + MaxValidators: stakingtypes.DefaultMaxValidators, + MaxEntries: stakingtypes.DefaultMaxEntries, + HistoricalEntries: stakingtypes.DefaultHistoricalEntries, + BondDenom: stakingtypes.BondStatusBonded, }, }, expErr: true, @@ -208,14 +68,14 @@ func TestMsgUpdateParams(t *testing.T) { }, { name: "invalid bond denom", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.Params{ - MinCommissionRate: types.DefaultMinCommissionRate, - UnbondingTime: types.DefaultUnbondingTime, - MaxValidators: types.DefaultMaxValidators, - MaxEntries: types.DefaultMaxEntries, - HistoricalEntries: types.DefaultHistoricalEntries, + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.Params{ + MinCommissionRate: stakingtypes.DefaultMinCommissionRate, + UnbondingTime: stakingtypes.DefaultUnbondingTime, + MaxValidators: stakingtypes.DefaultMaxValidators, + MaxEntries: stakingtypes.DefaultMaxEntries, + HistoricalEntries: stakingtypes.DefaultHistoricalEntries, BondDenom: "", }, }, @@ -224,15 +84,15 @@ func TestMsgUpdateParams(t *testing.T) { }, { name: "max validators most be positive", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.Params{ - MinCommissionRate: types.DefaultMinCommissionRate, - UnbondingTime: types.DefaultUnbondingTime, + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.Params{ + MinCommissionRate: stakingtypes.DefaultMinCommissionRate, + UnbondingTime: stakingtypes.DefaultUnbondingTime, MaxValidators: 0, - MaxEntries: types.DefaultMaxEntries, - HistoricalEntries: types.DefaultHistoricalEntries, - BondDenom: types.BondStatusBonded, + MaxEntries: stakingtypes.DefaultMaxEntries, + HistoricalEntries: stakingtypes.DefaultHistoricalEntries, + BondDenom: stakingtypes.BondStatusBonded, }, }, expErr: true, @@ -240,15 +100,15 @@ func TestMsgUpdateParams(t *testing.T) { }, { name: "max entries most be positive", - input: &types.MsgUpdateParams{ - Authority: app.StakingKeeper.GetAuthority(), - Params: types.Params{ - MinCommissionRate: types.DefaultMinCommissionRate, - UnbondingTime: types.DefaultUnbondingTime, - MaxValidators: types.DefaultMaxValidators, + input: &stakingtypes.MsgUpdateParams{ + Authority: keeper.GetAuthority(), + Params: stakingtypes.Params{ + MinCommissionRate: stakingtypes.DefaultMinCommissionRate, + UnbondingTime: stakingtypes.DefaultUnbondingTime, + MaxValidators: stakingtypes.DefaultMaxValidators, MaxEntries: 0, - HistoricalEntries: types.DefaultHistoricalEntries, - BondDenom: types.BondStatusBonded, + HistoricalEntries: stakingtypes.DefaultHistoricalEntries, + BondDenom: stakingtypes.BondStatusBonded, }, }, expErr: true, @@ -258,13 +118,13 @@ func TestMsgUpdateParams(t *testing.T) { for _, tc := range testCases { tc := tc - t.Run(tc.name, func(t *testing.T) { + s.T().Run(tc.name, func(t *testing.T) { _, err := msgServer.UpdateParams(ctx, tc.input) if tc.expErr { - require.Error(t, err) - require.Contains(t, err.Error(), tc.expErrMsg) + require.Error(err) + require.Contains(err.Error(), tc.expErrMsg) } else { - require.NoError(t, err) + require.NoError(err) } }) } diff --git a/x/staking/keeper/power_reduction_test.go b/x/staking/keeper/power_reduction_test.go index e18230d854b2d..1d777ebd64ff9 100644 --- a/x/staking/keeper/power_reduction_test.go +++ b/x/staking/keeper/power_reduction_test.go @@ -4,12 +4,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (suite *KeeperTestSuite) TestTokensToConsensusPower() { - suite.Require().Equal(int64(0), suite.app.StakingKeeper.TokensToConsensusPower(suite.ctx, sdk.DefaultPowerReduction.Sub(sdk.NewInt(1)))) - suite.Require().Equal(int64(1), suite.app.StakingKeeper.TokensToConsensusPower(suite.ctx, sdk.DefaultPowerReduction)) +func (s *KeeperTestSuite) TestTokensToConsensusPower() { + s.Require().Equal(int64(0), s.stakingKeeper.TokensToConsensusPower(s.ctx, sdk.DefaultPowerReduction.Sub(sdk.NewInt(1)))) + s.Require().Equal(int64(1), s.stakingKeeper.TokensToConsensusPower(s.ctx, sdk.DefaultPowerReduction)) } -func (suite *KeeperTestSuite) TestTokensFromConsensusPower() { - suite.Require().Equal(sdk.NewInt(0), suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 0)) - suite.Require().Equal(sdk.DefaultPowerReduction, suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 1)) +func (s *KeeperTestSuite) TestTokensFromConsensusPower() { + s.Require().Equal(sdk.NewInt(0), s.stakingKeeper.TokensFromConsensusPower(s.ctx, 0)) + s.Require().Equal(sdk.DefaultPowerReduction, s.stakingKeeper.TokensFromConsensusPower(s.ctx, 1)) } diff --git a/x/staking/keeper/slash_test.go b/x/staking/keeper/slash_test.go index fb1e67e861bc2..273256fb42eef 100644 --- a/x/staking/keeper/slash_test.go +++ b/x/staking/keeper/slash_test.go @@ -1,618 +1,50 @@ package keeper_test import ( - "testing" - "time" - - "cosmossdk.io/math" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// bootstrapSlashTest creates 3 validators and bootstrap the app. -func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { - _, app, ctx := createTestInput(t) - - addrDels, addrVals := generateAddresses(app, ctx, 100) - - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) - - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) - - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - numVals := int64(3) - bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals))) - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - - // set bonded pool balance - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins)) - - for i := int64(0); i < numVals; i++ { - validator := teststaking.NewValidator(t, addrVals[i], PKs[i]) - validator, _ = validator.AddTokensFromDel(amt) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) - } - - return app, ctx, addrDels, addrVals -} - // tests Jail, Unjail -func TestRevocation(t *testing.T) { - app, ctx, _, addrVals := bootstrapSlashTest(t, 5) +func (s *KeeperTestSuite) TestRevocation() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() + valAddr := sdk.ValAddress(PKs[0].Address().Bytes()) consAddr := sdk.ConsAddress(PKs[0].Address()) + validator := teststaking.NewValidator(s.T(), valAddr, PKs[0]) // initial state - val, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.False(t, val.IsJailed()) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByConsAddr(ctx, validator) + val, found := keeper.GetValidator(ctx, valAddr) + require.True(found) + require.False(val.IsJailed()) // test jail - app.StakingKeeper.Jail(ctx, consAddr) - val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.True(t, val.IsJailed()) + keeper.Jail(ctx, consAddr) + val, found = keeper.GetValidator(ctx, valAddr) + require.True(found) + require.True(val.IsJailed()) // test unjail - app.StakingKeeper.Unjail(ctx, consAddr) - val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.False(t, val.IsJailed()) -} - -// tests slashUnbondingDelegation -func TestSlashUnbondingDelegation(t *testing.T) { - app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) - - fraction := sdk.NewDecWithPrec(5, 1) - - // set an unbonding delegation with expiration timestamp (beyond which the - // unbonding delegation shouldn't be slashed) - ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, - time.Unix(5, 0), sdk.NewInt(10)) - - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - - // unbonding started prior to the infraction height, stakw didn't contribute - slashAmount := app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 1, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(0))) - - // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(0))) - - // test valid slash, before expiration timestamp and to which stake contributed - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(5))) - ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // initial balance unchanged - require.Equal(t, sdk.NewInt(10), ubd.Entries[0].InitialBalance) - - // balance decreased - require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance) - newUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - diffTokens := oldUnbondedPoolBalances.Sub(newUnbondedPoolBalances...) - require.True(t, diffTokens.AmountOf(app.StakingKeeper.BondDenom(ctx)).Equal(sdk.NewInt(5))) -} - -// tests slashRedelegation -func TestSlashRedelegation(t *testing.T) { - app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) - fraction := sdk.NewDecWithPrec(5, 1) - - // add bonded tokens to pool for (re)delegations - startCoins := sdk.NewCoins(sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 15)) - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), startCoins)) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - - // set a redelegation with an expiration timestamp beyond which the - // redelegation shouldn't be slashed - rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, - time.Unix(5, 0), sdk.NewInt(10), math.LegacyNewDec(10)) - - app.StakingKeeper.SetRedelegation(ctx, rd) - - // set the associated delegation - del := types.NewDelegation(addrDels[0], addrVals[1], math.LegacyNewDec(10)) - app.StakingKeeper.SetDelegation(ctx, del) - - // started redelegating prior to the current height, stake didn't contribute to infraction - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[1]) - require.True(t, found) - slashAmount := app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 1, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(0))) - - // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) - app.StakingKeeper.SetRedelegation(ctx, rd) - validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) - require.True(t, found) - slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(0))) - - balances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - - // test valid slash, before expiration timestamp and to which stake contributed - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) - app.StakingKeeper.SetRedelegation(ctx, rd) - validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) - require.True(t, found) - slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) - require.True(t, slashAmount.Equal(sdk.NewInt(5))) - rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rd.Entries, 1) - - // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - - // initialbalance unchanged - require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance) - - // shares decreased - del, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[1]) - require.True(t, found) - require.Equal(t, int64(5), del.Shares.RoundInt64()) - - // pool bonded tokens should decrease - burnedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), slashAmount)) - require.Equal(t, balances.Sub(burnedCoins...), app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())) + keeper.Unjail(ctx, consAddr) + val, found = keeper.GetValidator(ctx, valAddr) + require.True(found) + require.False(val.IsJailed()) } // tests Slash at a future height (must panic) -func TestSlashAtFutureHeight(t *testing.T) { - app, ctx, _, _ := bootstrapSlashTest(t, 10) - - consAddr := sdk.ConsAddress(PKs[0].Address()) - fraction := sdk.NewDecWithPrec(5, 1) - require.Panics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 1, 10, fraction) }) -} - -// test slash at a negative height -// this just represents pre-genesis and should have the same effect as slashing at height 0 -func TestSlashAtNegativeHeight(t *testing.T) { - app, ctx, _, _ := bootstrapSlashTest(t, 10) - consAddr := sdk.ConsAddress(PKs[0].Address()) - fraction := sdk.NewDecWithPrec(5, 1) +func (s *KeeperTestSuite) TestSlashAtFutureHeight() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - - validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - app.StakingKeeper.Slash(ctx, consAddr, -2, 10, fraction) - - // read updated state - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - - validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) - require.True(t, found) - // power decreased - require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // pool bonded shares decreased - newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String()) -} - -// tests Slash at the current height -func TestSlashValidatorAtCurrentHeight(t *testing.T) { - app, ctx, _, _ := bootstrapSlashTest(t, 10) consAddr := sdk.ConsAddress(PKs[0].Address()) - fraction := sdk.NewDecWithPrec(5, 1) - - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + validator := teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) + keeper.SetValidator(ctx, validator) + err := keeper.SetValidatorByConsAddr(ctx, validator) + require.NoError(err) - validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) - - // read updated state - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - - validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) - assert.True(t, found) - // power decreased - require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // pool bonded shares decreased - newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String()) -} - -// tests Slash at a previous height with an unbonding delegation -func TestSlashWithUnbondingDelegation(t *testing.T) { - app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) - - consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - - // set an unbonding delegation with expiration timestamp beyond which the - // unbonding delegation shouldn't be slashed - ubdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) - ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdTokens) - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - - // slash validator for the first time - ctx = ctx.WithBlockHeight(12) - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - - validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) - - // end block - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - - // read updating unbonding delegation - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // balance decreased - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 2), ubd.Entries[0].Balance) - - // bonded tokens burned - newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 3), diffTokens) - - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - // power decreased by 3 - 6 stake originally bonded at the time of infraction - // was still bonded at the time of discovery and was slashed by half, 4 stake - // bonded at the time of discovery hadn't been bonded at the time of infraction - // and wasn't slashed - require.Equal(t, int64(7), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // slash validator again - ctx = ctx.WithBlockHeight(13) - app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) - - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // balance decreased again - require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - - // bonded tokens burned again - newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 6), diffTokens) - - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - // power decreased by 3 again - require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // slash validator again - // all originally bonded stake has been slashed, so this will have no effect - // on the unbonding delegation, but it will slash stake bonded since the infraction - // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 - ctx = ctx.WithBlockHeight(13) - app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) - - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // balance unchanged - require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - - // bonded tokens burned again - newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), diffTokens) - - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - // power decreased by 3 again - require.Equal(t, int64(1), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // slash validator again - // all originally bonded stake has been slashed, so this will have no effect - // on the unbonding delegation, but it will slash stake bonded since the infraction - // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 - ctx = ctx.WithBlockHeight(13) - app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) - - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // balance unchanged - require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - - // just 1 bonded token burned again since that's all the validator now has - newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx)) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 10), diffTokens) - - // apply TM updates - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) - - // read updated validator - // power decreased by 1 again, validator is out of stake - // validator should be in unbonding period - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), types.Unbonding) -} - -// tests Slash at a previous height with a redelegation -func TestSlashWithRedelegation(t *testing.T) { - app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) - consAddr := sdk.ConsAddress(PKs[0].Address()) - fraction := sdk.NewDecWithPrec(5, 1) - bondDenom := app.StakingKeeper.BondDenom(ctx) - - // set a redelegation - rdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdTokens, sdk.NewDecFromInt(rdTokens)) - app.StakingKeeper.SetRedelegation(ctx, rd) - - // set the associated delegation - del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdTokens)) - app.StakingKeeper.SetDelegation(ctx, del) - - // update bonded tokens - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2))) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), rdCoins)) - - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - - oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - - // slash validator - ctx = ctx.WithBlockHeight(12) - validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) }) - burnAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt() - - bondedPool = app.StakingKeeper.GetBondedPool(ctx) - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - - // burn bonded tokens from only from delegations - bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) - - notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) - oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - - // read updating redelegation - rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rd.Entries, 1) - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - // power decreased by 2 - 4 stake originally bonded at the time of infraction - // was still bonded at the time of discovery and was slashed by half, 4 stake - // bonded at the time of discovery hadn't been bonded at the time of infraction - // and wasn't slashed - require.Equal(t, int64(8), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // slash the validator again - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) - burnAmount = app.StakingKeeper.TokensFromConsensusPower(ctx, 7) - - // read updated pool - bondedPool = app.StakingKeeper.GetBondedPool(ctx) - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - - // seven bonded tokens burned - bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) - require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) - - bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) - - notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) - oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - - // read updating redelegation - rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rd.Entries, 1) - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - // power decreased by 4 - require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - - // slash the validator again, by 100% - ctx = ctx.WithBlockHeight(12) - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.True(t, found) - - require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) - - burnAmount = sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(math.LegacyOneDec()).TruncateInt() - burnAmount = burnAmount.Sub(math.LegacyOneDec().MulInt(rdTokens).TruncateInt()) - - // read updated pool - bondedPool = app.StakingKeeper.GetBondedPool(ctx) - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - - bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) - notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) - oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - - // read updating redelegation - rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rd.Entries, 1) - // apply TM updates - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) - // read updated validator - // validator decreased to zero power, should be in unbonding period - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), types.Unbonding) - - // slash the validator again, by 100% - // no stake remains to be slashed - ctx = ctx.WithBlockHeight(12) - // validator still in unbonding period - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), types.Unbonding) - - require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) }) - - // read updated pool - bondedPool = app.StakingKeeper.GetBondedPool(ctx) - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - - bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded, bondedPoolBalance)) - notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance)) - - // read updating redelegation - rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rd.Entries, 1) - // read updated validator - // power still zero, still in unbonding period - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), types.Unbonding) -} - -// tests Slash at a previous height with both an unbonding delegation and a redelegation -func TestSlashBoth(t *testing.T) { - app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) - fraction := sdk.NewDecWithPrec(5, 1) - bondDenom := app.StakingKeeper.BondDenom(ctx) - - // set a redelegation with expiration timestamp beyond which the - // redelegation shouldn't be slashed - rdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6) - rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdATokens, sdk.NewDecFromInt(rdATokens)) - app.StakingKeeper.SetRedelegation(ctx, rdA) - - // set the associated delegation - delA := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdATokens)) - app.StakingKeeper.SetDelegation(ctx, delA) - - // set an unbonding delegation with expiration timestamp (beyond which the - // unbonding delegation shouldn't be slashed) - ubdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) - ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, - time.Unix(0, 0), ubdATokens) - app.StakingKeeper.SetUnbondingDelegation(ctx, ubdA) - - bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2))) - notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens)) - - // update bonded tokens - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins)) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), notBondedCoins)) - - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - // slash validator - ctx = ctx.WithBlockHeight(12) - validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) - require.True(t, found) - consAddr0 := sdk.ConsAddress(PKs[0].Address()) - app.StakingKeeper.Slash(ctx, consAddr0, 10, 10, fraction) - - burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt() - burnedBondAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt() - burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount) - - // read updated pool - bondedPool = app.StakingKeeper.GetBondedPool(ctx) - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - - bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPoolBalance)) - - notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount - require.True(math.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPoolBalance)) - - // read updating redelegation - rdA, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) - require.True(t, found) - require.Len(t, rdA.Entries, 1) - // read updated validator - validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) - require.True(t, found) - // power not decreased, all stake was bonded since - require.Equal(t, int64(10), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) -} - -func TestSlashAmount(t *testing.T) { - app, ctx, _, _ := bootstrapSlashTest(t, 10) - consAddr := sdk.ConsAddress(PKs[0].Address()) - fraction := sdk.NewDecWithPrec(5, 1) - burnedCoins := app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) - require.True(t, burnedCoins.GT(math.ZeroInt())) - - // test the case where the validator was not found, which should return no coins - _, addrVals := generateAddresses(app, ctx, 100) - noBurned := app.StakingKeeper.Slash(ctx, sdk.ConsAddress(addrVals[0]), ctx.BlockHeight(), 10, fraction) - require.True(t, sdk.NewInt(0).Equal(noBurned)) + require.Panics(func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) }) } diff --git a/x/staking/keeper/validator_test.go b/x/staking/keeper/validator_test.go index 58a1ccc4d05eb..bf9c6fa2fcf19 100644 --- a/x/staking/keeper/validator_test.go +++ b/x/staking/keeper/validator_test.go @@ -1,1083 +1,299 @@ package keeper_test import ( - "fmt" - "testing" "time" - "cosmossdk.io/math" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/golang/mock/gomock" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + abci "github.com/tendermint/tendermint/abci/types" ) -func newMonikerValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey, moniker string) types.Validator { - v, err := types.NewValidator(operator, pubKey, types.Description{Moniker: moniker}) - require.NoError(t, err) - return v -} - -func bootstrapValidatorTest(t testing.TB, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { - _, app, ctx := createTestInput(&testing.T{}) - - addrDels, addrVals := generateAddresses(app, ctx, numAddrs) - - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) - - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - // set bonded pool supply - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) - - // unbond genesis validator delegations - delegations := app.StakingKeeper.GetAllDelegations(ctx) - require.Len(t, delegations, 1) - delegation := delegations[0] - - _, err := app.StakingKeeper.Undelegate(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr(), delegation.Shares) - require.NoError(t, err) - - // end block to unbond genesis validator - staking.EndBlocker(ctx, app.StakingKeeper) - - return app, ctx, addrDels, addrVals -} - -func initValidators(t testing.TB, power int64, numAddrs int, powers []int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress, []types.Validator) { - app, ctx, addrs, valAddrs := bootstrapValidatorTest(t, power, numAddrs) - pks := simtestutil.CreateTestPubKeys(numAddrs) - - vs := make([]types.Validator, len(powers)) - for i, power := range powers { - vs[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), pks[i]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - vs[i], _ = vs[i].AddTokensFromDel(tokens) +func (s *KeeperTestSuite) applyValidatorSetUpdates(ctx sdk.Context, keeper *stakingkeeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate { + updates, err := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + s.Require().NoError(err) + if expectedUpdatesLen >= 0 { + s.Require().Equal(expectedUpdatesLen, len(updates), "%v", updates) } - return app, ctx, addrs, valAddrs, vs + return updates } -func TestSetValidator(t *testing.T) { - app, ctx, _, _ := bootstrapValidatorTest(t, 10, 100) +func (s *KeeperTestSuite) TestValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() valPubKey := PKs[0] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + valTokens := keeper.TokensFromConsensusPower(ctx, 10) // test how the validator is set from a purely unbonbed pool - validator := teststaking.NewValidator(t, valAddr, valPubKey) + validator := teststaking.NewValidator(s.T(), valAddr, valPubKey) validator, _ = validator.AddTokensFromDel(valTokens) - require.Equal(t, types.Unbonded, validator.Status) - assert.Equal(t, valTokens, validator.Tokens) - assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) - app.StakingKeeper.SetValidator(ctx, validator) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) + require.Equal(stakingtypes.Unbonded, validator.Status) + require.Equal(valTokens, validator.Tokens) + require.Equal(valTokens, validator.DelegatorShares.RoundInt()) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator) + keeper.SetValidatorByConsAddr(ctx, validator) // ensure update - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.Equal(t, validator.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + updates := s.applyValidatorSetUpdates(ctx, keeper, 1) + validator, found := keeper.GetValidator(ctx, valAddr) + require.True(found) + require.Equal(validator.ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[0]) // after the save the validator should be bonded - require.Equal(t, types.Bonded, validator.Status) - assert.Equal(t, valTokens, validator.Tokens) - assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) - - // Check each store for being saved - resVal, found := app.StakingKeeper.GetValidator(ctx, valAddr) - assert.True(ValEq(t, validator, resVal)) - require.True(t, found) - - resVals := app.StakingKeeper.GetLastValidators(ctx) - require.Equal(t, 1, len(resVals)) - assert.True(ValEq(t, validator, resVals[0])) - - resVals = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, 1, len(resVals)) - require.True(ValEq(t, validator, resVals[0])) - - resVals = app.StakingKeeper.GetValidators(ctx, 1) - require.Equal(t, 1, len(resVals)) - - resVals = app.StakingKeeper.GetValidators(ctx, 10) - require.Equal(t, 2, len(resVals)) - - allVals := app.StakingKeeper.GetAllValidators(ctx) - require.Equal(t, 2, len(allVals)) -} - -func TestUpdateValidatorByPowerIndex(t *testing.T) { - app, ctx, _, _ := bootstrapValidatorTest(t, 0, 100) - _, addrVals := generateAddresses(app, ctx, 1) - - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 1234))))) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 10000))))) - - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - // add a validator - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - validator, delSharesCreated := validator.AddTokensFromDel(app.StakingKeeper.TokensFromConsensusPower(ctx, 100)) - require.Equal(t, types.Unbonded, validator.Status) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 100), validator.Tokens) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 100), validator.Tokens) - - power := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) - require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) - - // burn half the delegator shares - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, burned := validator.RemoveDelShares(delSharesCreated.Quo(math.LegacyNewDec(2))) - require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 50), burned) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) // update the validator, possibly kicking it out - require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) - - validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - - power = types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) - require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) -} - -func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { - numVals := 10 - maxVals := 5 - - // create context, keeper, and pool for tests - app, ctx, _, valAddrs := bootstrapValidatorTest(t, 0, 100) - - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - // create keeper parameters - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = uint32(maxVals) - app.StakingKeeper.SetParams(ctx, params) - - // create a random pool - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 1234))))) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 10000))))) - - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - validators := make([]types.Validator, numVals) - for i := 0; i < len(validators); i++ { - moniker := fmt.Sprintf("val#%d", int64(i)) - val := newMonikerValidator(t, valAddrs[i], PKs[i], moniker) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, int64((i+1)*10)) - val, _ = val.AddTokensFromDel(delTokens) - - val = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, val, true) - validators[i] = val - } - - nextCliffVal := validators[numVals-maxVals+1] - - // remove enough tokens to kick out the validator below the current cliff - // validator and next in line cliff validator - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) - shares := app.StakingKeeper.TokensFromConsensusPower(ctx, 21) - nextCliffVal, _ = nextCliffVal.RemoveDelShares(sdk.NewDecFromInt(shares)) - nextCliffVal = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, nextCliffVal, true) - - expectedValStatus := map[int]types.BondStatus{ - 9: types.Bonded, 8: types.Bonded, 7: types.Bonded, 5: types.Bonded, 4: types.Bonded, - 0: types.Unbonding, 1: types.Unbonding, 2: types.Unbonding, 3: types.Unbonding, 6: types.Unbonding, - } - - // require all the validators have their respective statuses - for valIdx, status := range expectedValStatus { - valAddr := validators[valIdx].OperatorAddress - addr, err := sdk.ValAddressFromBech32(valAddr) - assert.NoError(t, err) - val, _ := app.StakingKeeper.GetValidator(ctx, addr) - - assert.Equal( - t, status, val.GetStatus(), - fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status), - ) - } -} - -func TestSlashToZeroPowerRemoved(t *testing.T) { - // initialize setup - app, ctx, _, addrVals := bootstrapValidatorTest(t, 100, 20) - - // add a validator - validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens)))) - - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - - validator, _ = validator.AddTokensFromDel(valTokens) - require.Equal(t, types.Unbonded, validator.Status) - require.Equal(t, valTokens, validator.Tokens) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) - validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, valTokens) - - // slash the validator by 100% - app.StakingKeeper.Slash(ctx, sdk.ConsAddress(PKs[0].Address()), 0, 100, math.LegacyOneDec()) - // apply TM updates - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) - // validator should be unbonding - validator, _ = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.Equal(t, validator.GetStatus(), types.Unbonding) + require.Equal(stakingtypes.Bonded, validator.Status) + require.Equal(valTokens, validator.Tokens) + require.Equal(valTokens, validator.DelegatorShares.RoundInt()) + + // check each store for being saved + consAddr, err := validator.GetConsAddr() + require.NoError(err) + resVal, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(found) + require.True(validator.MinEqual(&resVal)) + + resVals := keeper.GetLastValidators(ctx) + require.Equal(1, len(resVals)) + require.True(validator.MinEqual(&resVals[0])) + + resVals = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(1, len(resVals)) + require.True(validator.MinEqual(&resVals[0])) + + allVals := keeper.GetAllValidators(ctx) + require.Equal(1, len(allVals)) + + // check the last validator power + power := int64(100) + keeper.SetLastValidatorPower(ctx, valAddr, power) + resPower := keeper.GetLastValidatorPower(ctx, valAddr) + require.Equal(power, resPower) + keeper.DeleteLastValidatorPower(ctx, valAddr) + resPower = keeper.GetLastValidatorPower(ctx, valAddr) + require.Equal(int64(0), resPower) } // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator -func TestValidatorBasics(t *testing.T) { - app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20) +func (s *KeeperTestSuite) TestValidatorBasics() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() // construct the validators var validators [3]types.Validator powers := []int64{9, 8, 7} for i, power := range powers { - validators[i] = teststaking.NewValidator(t, addrVals[i], PKs[i]) + validators[i] = teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[i].Address().Bytes()), PKs[i]) validators[i].Status = types.Unbonded validators[i].Tokens = math.ZeroInt() - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + tokens := keeper.TokensFromConsensusPower(ctx, power) validators[i], _ = validators[i].AddTokensFromDel(tokens) } - assert.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), validators[0].Tokens) - assert.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 8), validators[1].Tokens) - assert.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 7), validators[2].Tokens) + + require.Equal(keeper.TokensFromConsensusPower(ctx, 9), validators[0].Tokens) + require.Equal(keeper.TokensFromConsensusPower(ctx, 8), validators[1].Tokens) + require.Equal(keeper.TokensFromConsensusPower(ctx, 7), validators[2].Tokens) // check the empty keeper first - _, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.False(t, found) - resVals := app.StakingKeeper.GetLastValidators(ctx) - require.Zero(t, len(resVals)) + _, found := keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) + require.False(found) + resVals := keeper.GetLastValidators(ctx) + require.Zero(len(resVals)) - resVals = app.StakingKeeper.GetValidators(ctx, 2) - require.Len(t, resVals, 1) + resVals = keeper.GetValidators(ctx, 2) + require.Len(resVals, 0) // set and retrieve a record - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - app.StakingKeeper.SetValidatorByConsAddr(ctx, validators[0]) - resVal, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - assert.True(ValEq(t, validators[0], resVal)) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], true) + keeper.SetValidatorByConsAddr(ctx, validators[0]) + resVal, found := keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) + require.True(found) + require.True(validators[0].MinEqual(&resVal)) // retrieve from consensus - resVal, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) - require.True(t, found) - assert.True(ValEq(t, validators[0], resVal)) - resVal, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) - require.True(t, found) - assert.True(ValEq(t, validators[0], resVal)) - - resVals = app.StakingKeeper.GetLastValidators(ctx) - require.Equal(t, 1, len(resVals)) - assert.True(ValEq(t, validators[0], resVals[0])) - assert.Equal(t, types.Bonded, validators[0].Status) - assert.True(math.IntEq(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), validators[0].BondedTokens())) + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) + require.True(found) + require.True(validators[0].MinEqual(&resVal)) + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(found) + require.True(validators[0].MinEqual(&resVal)) + + resVals = keeper.GetLastValidators(ctx) + require.Equal(1, len(resVals)) + require.True(validators[0].MinEqual(&resVals[0])) + require.Equal(types.Bonded, validators[0].Status) + require.True(keeper.TokensFromConsensusPower(ctx, 9).Equal(validators[0].BondedTokens())) // modify a records, save, and retrieve validators[0].Status = types.Bonded - validators[0].Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 10) + validators[0].Tokens = keeper.TokensFromConsensusPower(ctx, 10) validators[0].DelegatorShares = sdk.NewDecFromInt(validators[0].Tokens) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) - require.True(t, found) - assert.True(ValEq(t, validators[0], resVal)) + validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], true) + resVal, found = keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) + require.True(found) + require.True(validators[0].MinEqual(&resVal)) - resVals = app.StakingKeeper.GetLastValidators(ctx) - require.Equal(t, 1, len(resVals)) - assert.True(ValEq(t, validators[0], resVals[0])) + resVals = keeper.GetLastValidators(ctx) + require.Equal(1, len(resVals)) + require.True(validators[0].MinEqual(&resVals[0])) // add other validators - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) - validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) - resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) - require.True(t, found) - assert.True(ValEq(t, validators[1], resVal)) - resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[2]) - require.True(t, found) - assert.True(ValEq(t, validators[2], resVal)) - - resVals = app.StakingKeeper.GetLastValidators(ctx) - require.Equal(t, 3, len(resVals)) - assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here - assert.True(ValEq(t, validators[1], resVals[1])) - assert.True(ValEq(t, validators[2], resVals[2])) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], true) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validators[2] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[2], true) + resVal, found = keeper.GetValidator(ctx, sdk.ValAddress(PKs[1].Address().Bytes())) + require.True(found) + require.True(validators[1].MinEqual(&resVal)) + resVal, found = keeper.GetValidator(ctx, sdk.ValAddress(PKs[2].Address().Bytes())) + require.True(found) + require.True(validators[2].MinEqual(&resVal)) + + resVals = keeper.GetLastValidators(ctx) + require.Equal(3, len(resVals)) // remove a record // shouldn't be able to remove if status is not unbonded - assert.PanicsWithValue(t, - "cannot call RemoveValidator on bonded or unbonding validators", - func() { app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) }) + require.PanicsWithValue("cannot call RemoveValidator on bonded or unbonding validators", + func() { keeper.RemoveValidator(ctx, validators[1].GetOperator()) }) // shouldn't be able to remove if there are still tokens left validators[1].Status = types.Unbonded - app.StakingKeeper.SetValidator(ctx, validators[1]) - assert.PanicsWithValue(t, - "attempting to remove a validator which still contains tokens", - func() { app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) }) - - validators[1].Tokens = math.ZeroInt() // ...remove all tokens - app.StakingKeeper.SetValidator(ctx, validators[1]) // ...set the validator - app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) // Now it can be removed. - _, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) - require.False(t, found) -} - -// test how the validators are sorted, tests GetBondedValidatorsByPower -func TestGetValidatorSortingUnmixed(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - - // initialize some validators into the state - amts := []math.Int{ - sdk.NewIntFromUint64(0), - app.StakingKeeper.PowerReduction(ctx).MulRaw(100), - app.StakingKeeper.PowerReduction(ctx), - app.StakingKeeper.PowerReduction(ctx).MulRaw(400), - app.StakingKeeper.PowerReduction(ctx).MulRaw(200), - } - n := len(amts) - var validators [5]types.Validator - for i, amt := range amts { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - validators[i].Status = types.Bonded - validators[i].Tokens = amt - validators[i].DelegatorShares = sdk.NewDecFromInt(amt) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) - } - - // first make sure everything made it in to the gotValidator group - resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) - assert.Equal(t, n, len(resValidators)) - assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewInt(100).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[2].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewInt(1).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[3].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewInt(0), resValidators[4].BondedTokens(), "%v", resValidators) - assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) - assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) - assert.Equal(t, validators[1].OperatorAddress, resValidators[2].OperatorAddress, "%v", resValidators) - assert.Equal(t, validators[2].OperatorAddress, resValidators[3].OperatorAddress, "%v", resValidators) - assert.Equal(t, validators[0].OperatorAddress, resValidators[4].OperatorAddress, "%v", resValidators) - - // test a basic increase in voting power - validators[3].Tokens = sdk.NewInt(500).Mul(app.StakingKeeper.PowerReduction(ctx)) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n) - assert.True(ValEq(t, validators[3], resValidators[0])) - - // test a decrease in voting power - validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n) - assert.True(ValEq(t, validators[3], resValidators[0])) - assert.True(ValEq(t, validators[4], resValidators[1])) - - // test equal voting power, different age - validators[3].Tokens = sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)) - ctx = ctx.WithBlockHeight(10) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n) - assert.True(ValEq(t, validators[3], resValidators[0])) - assert.True(ValEq(t, validators[4], resValidators[1])) - - // no change in voting power - no change in sort - ctx = ctx.WithBlockHeight(20) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n) - assert.True(ValEq(t, validators[3], resValidators[0])) - assert.True(ValEq(t, validators[4], resValidators[1])) - - // change in voting power of both validators, both still in v-set, no age change - validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) - validators[4].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx)) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n) - ctx = ctx.WithBlockHeight(30) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, len(resValidators), n, "%v", resValidators) - assert.True(ValEq(t, validators[3], resValidators[0])) - assert.True(ValEq(t, validators[4], resValidators[1])) -} - -func TestGetValidatorSortingMixed(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 501))))) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 0))))) - - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - - // now 2 max resValidators - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = 2 - app.StakingKeeper.SetParams(ctx, params) - - // initialize some validators into the state - amts := []math.Int{ - sdk.NewIntFromUint64(0), - app.StakingKeeper.PowerReduction(ctx).MulRaw(100), - app.StakingKeeper.PowerReduction(ctx), - app.StakingKeeper.PowerReduction(ctx).MulRaw(400), - app.StakingKeeper.PowerReduction(ctx).MulRaw(200), - } - - var validators [5]types.Validator - for i, amt := range amts { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - validators[i].DelegatorShares = sdk.NewDecFromInt(amt) - validators[i].Status = types.Bonded - validators[i].Tokens = amt - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) - } - - val0, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0])) - require.True(t, found) - val1, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1])) - require.True(t, found) - val2, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[2])) - require.True(t, found) - val3, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[3])) - require.True(t, found) - val4, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[4])) - require.True(t, found) - require.Equal(t, types.Bonded, val0.Status) - require.Equal(t, types.Unbonding, val1.Status) - require.Equal(t, types.Unbonding, val2.Status) - require.Equal(t, types.Bonded, val3.Status) - require.Equal(t, types.Bonded, val4.Status) - - // first make sure everything made it in to the gotValidator group - resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) - // The validators returned should match the max validators - assert.Equal(t, 2, len(resValidators)) - assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) - assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) -} - -// TODO separate out into multiple tests -func TestGetValidatorsEdgeCases(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - - // set max validators to 2 - params := app.StakingKeeper.GetParams(ctx) - nMax := uint32(2) - params.MaxValidators = nMax - app.StakingKeeper.SetParams(ctx, params) - - // initialize some validators into the state - powers := []int64{0, 100, 400, 400} - var validators [4]types.Validator - for i, power := range powers { - moniker := fmt.Sprintf("val#%d", int64(i)) - validators[i] = newMonikerValidator(t, sdk.ValAddress(addrs[i]), PKs[i], moniker) - - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, tokens)))) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - validators[i] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) - } - - // ensure that the first two bonded validators are the largest validators - resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint32(len(resValidators))) - assert.True(ValEq(t, validators[2], resValidators[0])) - assert.True(ValEq(t, validators[3], resValidators[1])) - - // delegate 500 tokens to validator 0 - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500) - validators[0], _ = validators[0].AddTokensFromDel(delTokens) - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - newTokens := sdk.NewCoins() - - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - // test that the two largest validators are - // a) validator 0 with 500 tokens - // b) validator 2 with 400 tokens (delegated before validator 3) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint32(len(resValidators))) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[2], resValidators[1])) - - // A validator which leaves the bonded validator set due to a decrease in voting power, - // then increases to the original voting power, does not get its spot back in the - // case of a tie. - // - // Order of operations for this test: - // - validator 3 enter validator set with 1 new token - // - validator 3 removed validator set by removing 201 tokens (validator 2 enters) - // - validator 3 adds 200 tokens (equal to validator 2 now) and does not get its spot back - - // validator 3 enters bonded validator set - ctx = ctx.WithBlockHeight(40) - - var found bool - validators[3], found = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) - assert.True(t, found) - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) - validators[3], _ = validators[3].AddTokensFromDel(app.StakingKeeper.TokensFromConsensusPower(ctx, 1)) - - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 1))) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens)) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint32(len(resValidators))) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[3], resValidators[1])) - - // validator 3 kicked out temporarily - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) - rmTokens := validators[3].TokensFromShares(math.LegacyNewDec(201)).TruncateInt() - validators[3], _ = validators[3].RemoveDelShares(math.LegacyNewDec(201)) - - bondedPool := app.StakingKeeper.GetBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, rmTokens)))) - app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - - validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint32(len(resValidators))) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[2], resValidators[1])) - - // validator 3 does not get spot back - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) - validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200)) - - notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.NewInt(200))))) - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - - validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint32(len(resValidators))) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[2], resValidators[1])) - _, exists := app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) - require.True(t, exists) + keeper.SetValidator(ctx, validators[1]) + require.PanicsWithValue("attempting to remove a validator which still contains tokens", + func() { keeper.RemoveValidator(ctx, validators[1].GetOperator()) }) + + validators[1].Tokens = math.ZeroInt() // ...remove all tokens + keeper.SetValidator(ctx, validators[1]) // ...set the validator + keeper.RemoveValidator(ctx, validators[1].GetOperator()) // Now it can be removed. + _, found = keeper.GetValidator(ctx, sdk.ValAddress(PKs[1].Address().Bytes())) + require.False(found) } -func TestValidatorBondHeight(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) +func (s *KeeperTestSuite) TestUpdateValidatorByPowerIndex() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - // now 2 max resValidators - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = 2 - app.StakingKeeper.SetParams(ctx, params) + valPubKey := PKs[0] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + valTokens := keeper.TokensFromConsensusPower(ctx, 100) - // initialize some validators into the state - var validators [3]types.Validator - validators[0] = teststaking.NewValidator(t, sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) - validators[1] = teststaking.NewValidator(t, sdk.ValAddress(addrs[1]), PKs[1]) - validators[2] = teststaking.NewValidator(t, sdk.ValAddress(addrs[2]), PKs[2]) - - tokens0 := app.StakingKeeper.TokensFromConsensusPower(ctx, 200) - tokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - tokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - validators[0], _ = validators[0].AddTokensFromDel(tokens0) - validators[1], _ = validators[1].AddTokensFromDel(tokens1) - validators[2], _ = validators[2].AddTokensFromDel(tokens2) - - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - - //////////////////////////////////////// - // If two validators both increase to the same voting power in the same block, - // the one with the first transaction should become bonded - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) - validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) - - resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, uint32(len(resValidators)), params.MaxValidators) - - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[1], resValidators[1])) - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[2]) - delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 50) - validators[1], _ = validators[1].AddTokensFromDel(delTokens) - validators[2], _ = validators[2].AddTokensFromDel(delTokens) - validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, params.MaxValidators, uint32(len(resValidators))) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[2], resValidators[1])) -} + // add a validator + validator := teststaking.NewValidator(s.T(), valAddr, PKs[0]) + validator, delSharesCreated := validator.AddTokensFromDel(valTokens) + require.Equal(stakingtypes.Unbonded, validator.Status) + require.Equal(valTokens, validator.Tokens) -func TestFullValidatorSetPowerChange(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - params := app.StakingKeeper.GetParams(ctx) - max := 2 - params.MaxValidators = uint32(2) - app.StakingKeeper.SetParams(ctx, params) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + validator, found := keeper.GetValidator(ctx, valAddr) + require.True(found) + require.Equal(valTokens, validator.Tokens) - // initialize some validators into the state - powers := []int64{0, 100, 400, 400, 200} - var validators [5]types.Validator - for i, power := range powers { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) - } - for i := range powers { - var found bool - validators[i], found = app.StakingKeeper.GetValidator(ctx, validators[i].GetOperator()) - require.True(t, found) - } - assert.Equal(t, types.Unbonded, validators[0].Status) - assert.Equal(t, types.Unbonding, validators[1].Status) - assert.Equal(t, types.Bonded, validators[2].Status) - assert.Equal(t, types.Bonded, validators[3].Status) - assert.Equal(t, types.Unbonded, validators[4].Status) - resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) - assert.Equal(t, max, len(resValidators)) - assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs - assert.True(ValEq(t, validators[3], resValidators[1])) - - // test a swap in voting power - - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 600) - validators[0], _ = validators[0].AddTokensFromDel(tokens) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) - resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) - assert.Equal(t, max, len(resValidators)) - assert.True(ValEq(t, validators[0], resValidators[0])) - assert.True(ValEq(t, validators[2], resValidators[1])) -} + power := stakingtypes.GetValidatorsByPowerIndexKey(validator, keeper.PowerReduction(ctx)) + require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) -func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { - app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + // burn half the delegator shares + keeper.DeleteValidatorByPowerIndex(ctx, validator) + validator, burned := validator.RemoveDelShares(delSharesCreated.Quo(math.LegacyNewDec(2))) + require.Equal(keeper.TokensFromConsensusPower(ctx, 50), burned) + stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) // update the validator, possibly kicking it out + require.False(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) - powers := []int64{10, 20} - var validators [2]types.Validator - for i, power := range powers { - valPubKey := PKs[i+1] - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(found) - validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - } + power = stakingtypes.GetValidatorsByPowerIndexKey(validator, keeper.PowerReduction(ctx)) + require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) - // test from nothing to something - // tendermintUpdate set: {} -> {c1, c3} - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - app.StakingKeeper.SetValidator(ctx, validators[0]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) - app.StakingKeeper.SetValidator(ctx, validators[1]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) - - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) - validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) - assert.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) - assert.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) + // set new validator by power index + keeper.DeleteValidatorByPowerIndex(ctx, validator) + require.False(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) + keeper.SetNewValidatorByPowerIndex(ctx, validator) + require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) } -func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) +func (s *KeeperTestSuite) TestApplyAndReturnValidatorSetUpdatesPowerDecrease() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() - powers := []int64{10, 20} + powers := []int64{100, 100} var validators [2]types.Validator - for i, power := range powers { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - } - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - - // test identical, - // tendermintUpdate set: {} -> {} - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) -} - -func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - powers := []int64{10, 20} - var validators [2]types.Validator for i, power := range powers { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) + validators[i] = teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[i].Address().Bytes()), PKs[i]) + tokens := keeper.TokensFromConsensusPower(ctx, power) validators[i], _ = validators[i].AddTokensFromDel(tokens) } - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - - // test single value change - // tendermintUpdate set: {} -> {c1'} - validators[0].Status = types.Bonded - validators[0].Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 600) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) -} - -func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { - powers := []int64{10, 20} - // TODO: use it in other places - app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) - - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - - // test multiple value change - // tendermintUpdate set: {c1, c3} -> {c1', c3'} - delTokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 190) - delTokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 80) - validators[0], _ = validators[0].AddTokensFromDel(delTokens1) - validators[1], _ = validators[1].AddTokensFromDel(delTokens2) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) -} - -func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { - powers := []int64{10, 20, 5, 15, 25} - app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) - - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - - // test validtor added at the beginning - // tendermintUpdate set: {} -> {c0} - app.StakingKeeper.SetValidator(ctx, validators[2]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) - require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - - // test validtor added at the beginning - // tendermintUpdate set: {} -> {c0} - app.StakingKeeper.SetValidator(ctx, validators[3]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[3]) - updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validators[3], _ = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) - require.Equal(t, validators[3].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - - // test validtor added at the end - // tendermintUpdate set: {} -> {c0} - app.StakingKeeper.SetValidator(ctx, validators[4]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[4]) - updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validators[4], _ = app.StakingKeeper.GetValidator(ctx, validators[4].GetOperator()) - require.Equal(t, validators[4].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) -} - -func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - params := types.DefaultParams() - params.MaxValidators = 2 - app.StakingKeeper.SetParams(ctx, params) - - powers := []int64{10, 20, 5} - var validators [5]types.Validator - for i, power := range powers { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - } - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - - // test validator added at the end but not inserted in the valset - // tendermintUpdate set: {} -> {} - keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - // test validator change its power and become a gotValidator (pushing out an existing) - // tendermintUpdate set: {} -> {c0, c4} - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - validators[2], _ = validators[2].AddTokensFromDel(tokens) - app.StakingKeeper.SetValidator(ctx, validators[2]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) - require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1]) - require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) -} - -func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { - app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) - powers := []int64{100, 100} - var validators [2]types.Validator - for i, power := range powers { - validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - } - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], false) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], false) + s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) + s.applyValidatorSetUpdates(ctx, keeper, 2) // check initial power - require.Equal(t, int64(100), validators[0].GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - require.Equal(t, int64(100), validators[1].GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + require.Equal(int64(100), validators[0].GetConsensusPower(keeper.PowerReduction(ctx))) + require.Equal(int64(100), validators[1].GetConsensusPower(keeper.PowerReduction(ctx))) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} - delTokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 20) - delTokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) + delTokens1 := keeper.TokensFromConsensusPower(ctx, 20) + delTokens2 := keeper.TokensFromConsensusPower(ctx, 30) validators[0], _ = validators[0].RemoveDelShares(sdk.NewDecFromInt(delTokens1)) validators[1], _ = validators[1].RemoveDelShares(sdk.NewDecFromInt(delTokens2)) - validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], false) // power has changed - require.Equal(t, int64(80), validators[0].GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) - require.Equal(t, int64(70), validators[1].GetConsensusPower(app.StakingKeeper.PowerReduction(ctx))) + require.Equal(int64(80), validators[0].GetConsensusPower(keeper.PowerReduction(ctx))) + require.Equal(int64(70), validators[1].GetConsensusPower(keeper.PowerReduction(ctx))) // Tendermint updates should reflect power change - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) + updates := s.applyValidatorSetUpdates(ctx, keeper, 2) + require.Equal(validators[0].ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[0]) + require.Equal(validators[1].ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[1]) } -func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { - app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = uint32(3) - - app.StakingKeeper.SetParams(ctx, params) - - powers := []int64{100, 100} - var validators [2]types.Validator - - // initialize some validators into the state - for i, power := range powers { - valPubKey := PKs[i+1] - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - - validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - app.StakingKeeper.SetValidator(ctx, validators[i]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) - } - - // verify initial Tendermint updates are correct - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)) - validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) - validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) - require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) - - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - // update initial validator set - for i, power := range powers { - - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[i]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - app.StakingKeeper.SetValidator(ctx, validators[i]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) - } - - // add a new validator that goes from zero power, to non-zero power, back to - // zero power - valPubKey := PKs[len(validators)+1] - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - amt := sdk.NewInt(100) - - validator := teststaking.NewValidator(t, valAddr, valPubKey) - validator, _ = validator.AddTokensFromDel(amt) - - app.StakingKeeper.SetValidator(ctx, validator) - - validator, _ = validator.RemoveDelShares(sdk.NewDecFromInt(amt)) - app.StakingKeeper.SetValidator(ctx, validator) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) - - // add a new validator that increases in power - valPubKey = PKs[len(validators)+2] - valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) - - validator = teststaking.NewValidator(t, valAddr, valPubKey) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500) - validator, _ = validator.AddTokensFromDel(tokens) - app.StakingKeeper.SetValidator(ctx, validator) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) - - // verify initial Tendermint updates are correct - updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)+1) - validator, _ = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) - validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) - validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) - require.Equal(t, validator.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[2]) -} - -func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { - app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = uint32(2) - - app.StakingKeeper.SetParams(ctx, params) - - powers := []int64{100, 200, 300} - var validators [3]types.Validator - - // initialize some validators into the state - for i, power := range powers { - moniker := fmt.Sprintf("%d", i) - valPubKey := PKs[i+1] - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - - validators[i] = newMonikerValidator(t, valAddr, valPubKey, moniker) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - app.StakingKeeper.SetValidator(ctx, validators[i]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) - } - - // verify initial Tendermint updates are correct - updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) - validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) - require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1]) - - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - // delegate to validator with lowest power but not enough to bond - ctx = ctx.WithBlockHeight(1) - - var found bool - validators[0], found = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) - require.True(t, found) - - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) - tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1) - validators[0], _ = validators[0].AddTokensFromDel(tokens) - app.StakingKeeper.SetValidator(ctx, validators[0]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) - - // verify initial Tendermint updates are correct - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - // create a series of events that will bond and unbond the validator with - // lowest power in a single block context (height) - ctx = ctx.WithBlockHeight(2) - - validators[1], found = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) - require.True(t, found) - - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) - validators[0], _ = validators[0].RemoveDelShares(validators[0].DelegatorShares) - app.StakingKeeper.SetValidator(ctx, validators[0]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - - app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) - tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 250) - validators[1], _ = validators[1].AddTokensFromDel(tokens) - app.StakingKeeper.SetValidator(ctx, validators[1]) - app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) - - // verify initial Tendermint updates are correct - updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0]) - - applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) -} - -func TestUpdateValidatorCommission(t *testing.T) { - app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20) - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Now().UTC()}) +func (s *KeeperTestSuite) TestUpdateValidatorCommission() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() // Set MinCommissionRate to 0.05 - params := app.StakingKeeper.GetParams(ctx) + params := keeper.GetParams(ctx) params.MinCommissionRate = sdk.NewDecWithPrec(5, 2) - app.StakingKeeper.SetParams(ctx, params) + keeper.SetParams(ctx, params) - commission1 := types.NewCommissionWithTime( + commission1 := stakingtypes.NewCommissionWithTime( sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1), time.Now().UTC().Add(time.Duration(-1)*time.Hour), ) - commission2 := types.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1)) + commission2 := stakingtypes.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1)) - val1 := teststaking.NewValidator(t, addrVals[0], PKs[0]) - val2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) + val1 := teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) + val2 := teststaking.NewValidator(s.T(), sdk.ValAddress(PKs[1].Address().Bytes()), PKs[1]) val1, _ = val1.SetInitialCommission(commission1) val2, _ = val2.SetInitialCommission(commission2) - app.StakingKeeper.SetValidator(ctx, val1) - app.StakingKeeper.SetValidator(ctx, val2) + keeper.SetValidator(ctx, val1) + keeper.SetValidator(ctx, val2) testCases := []struct { - validator types.Validator + validator stakingtypes.Validator newRate sdk.Dec expectedErr bool }{ @@ -1090,36 +306,115 @@ func TestUpdateValidatorCommission(t *testing.T) { } for i, tc := range testCases { - commission, err := app.StakingKeeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) + commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) if tc.expectedErr { - require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate) + require.Error(err, "expected error for test case #%d with rate: %s", i, tc.newRate) } else { tc.validator.Commission = commission - app.StakingKeeper.SetValidator(ctx, tc.validator) - val, found := app.StakingKeeper.GetValidator(ctx, tc.validator.GetOperator()) + keeper.SetValidator(ctx, tc.validator) + val, found := keeper.GetValidator(ctx, tc.validator.GetOperator()) - require.True(t, found, + require.True(found, "expected to find validator for test case #%d with rate: %s", i, tc.newRate, ) - require.NoError(t, err, + require.NoError(err, "unexpected error for test case #%d with rate: %s", i, tc.newRate, ) - require.Equal(t, tc.newRate, val.Commission.Rate, + require.Equal(tc.newRate, val.Commission.Rate, "expected new validator commission rate for test case #%d with rate: %s", i, tc.newRate, ) - require.Equal(t, ctx.BlockHeader().Time, val.Commission.UpdateTime, + require.Equal(ctx.BlockHeader().Time, val.Commission.UpdateTime, "expected new validator commission update time for test case #%d with rate: %s", i, tc.newRate, ) } } } -func applyValidatorSetUpdates(t *testing.T, ctx sdk.Context, k *keeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate { - updates, err := k.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - if expectedUpdatesLen >= 0 { - require.Equal(t, expectedUpdatesLen, len(updates), "%v", updates) - } - return updates +func (s *KeeperTestSuite) TestValidatorToken() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() + + valPubKey := PKs[0] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + addTokens := keeper.TokensFromConsensusPower(ctx, 10) + delTokens := keeper.TokensFromConsensusPower(ctx, 5) + + validator := teststaking.NewValidator(s.T(), valAddr, valPubKey) + validator, _ = keeper.AddValidatorTokensAndShares(ctx, validator, addTokens) + require.Equal(addTokens, validator.Tokens) + validator, _ = keeper.GetValidator(ctx, valAddr) + require.Equal(sdk.NewDecFromInt(addTokens), validator.DelegatorShares) + + keeper.RemoveValidatorTokensAndShares(ctx, validator, sdk.NewDecFromInt(delTokens)) + validator, _ = keeper.GetValidator(ctx, valAddr) + require.Equal(delTokens, validator.Tokens) + require.True(validator.DelegatorShares.Equal(sdk.NewDecFromInt(delTokens))) + + keeper.RemoveValidatorTokens(ctx, validator, delTokens) + validator, _ = keeper.GetValidator(ctx, valAddr) + require.True(validator.Tokens.IsZero()) +} + +func (s *KeeperTestSuite) TestUnbondingValidator() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() + + valPubKey := PKs[0] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + validator := teststaking.NewValidator(s.T(), valAddr, valPubKey) + addTokens := keeper.TokensFromConsensusPower(ctx, 10) + + // set unbonding validator + endTime := time.Now() + endHeight := ctx.BlockHeight() + 10 + keeper.SetUnbondingValidatorsQueue(ctx, endTime, endHeight, []string{valAddr.String()}) + + resVals := keeper.GetUnbondingValidators(ctx, endTime, endHeight) + require.Equal(1, len(resVals)) + require.Equal(valAddr.String(), resVals[0]) + + // add another unbonding validator + valAddr1 := sdk.ValAddress(PKs[1].Address().Bytes()) + validator1 := teststaking.NewValidator(s.T(), valAddr1, PKs[1]) + validator1.UnbondingHeight = endHeight + validator1.UnbondingTime = endTime + keeper.InsertUnbondingValidatorQueue(ctx, validator1) + + resVals = keeper.GetUnbondingValidators(ctx, endTime, endHeight) + require.Equal(2, len(resVals)) + + // delete unbonding validator from the queue + keeper.DeleteValidatorQueue(ctx, validator1) + resVals = keeper.GetUnbondingValidators(ctx, endTime, endHeight) + require.Equal(1, len(resVals)) + require.Equal(valAddr.String(), resVals[0]) + + // check unbonding mature validators + ctx = ctx.WithBlockHeight(endHeight).WithBlockTime(endTime) + require.PanicsWithValue("validator in the unbonding queue was not found", func() { + keeper.UnbondAllMatureValidators(ctx) + }) + + keeper.SetValidator(ctx, validator) + ctx = ctx.WithBlockHeight(endHeight).WithBlockTime(endTime) + require.PanicsWithValue("unexpected validator in unbonding queue; status was not unbonding", func() { + keeper.UnbondAllMatureValidators(ctx) + }) + + validator.Status = stakingtypes.Unbonding + keeper.SetValidator(ctx, validator) + keeper.UnbondAllMatureValidators(ctx) + validator, found := keeper.GetValidator(ctx, valAddr) + require.False(found) + + keeper.SetUnbondingValidatorsQueue(ctx, endTime, endHeight, []string{valAddr.String()}) + validator = teststaking.NewValidator(s.T(), valAddr, valPubKey) + validator, _ = validator.AddTokensFromDel(addTokens) + validator.Status = stakingtypes.Unbonding + keeper.SetValidator(ctx, validator) + keeper.UnbondAllMatureValidators(ctx) + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(found) + require.Equal(stakingtypes.Unbonded, validator.Status) } diff --git a/x/staking/testutil/app_config.go b/x/staking/testutil/app_config.go new file mode 100644 index 0000000000000..32507cd4e8a34 --- /dev/null +++ b/x/staking/testutil/app_config.go @@ -0,0 +1,122 @@ +package testutil + +import ( + _ "github.com/cosmos/cosmos-sdk/x/auth" + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/module" + _ "github.com/cosmos/cosmos-sdk/x/bank" + _ "github.com/cosmos/cosmos-sdk/x/distribution" + _ "github.com/cosmos/cosmos-sdk/x/genutil" + _ "github.com/cosmos/cosmos-sdk/x/mint" + _ "github.com/cosmos/cosmos-sdk/x/params" + _ "github.com/cosmos/cosmos-sdk/x/slashing" + _ "github.com/cosmos/cosmos-sdk/x/staking" + + "cosmossdk.io/core/appconfig" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1" + appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" + authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1" + bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1" + distrmodulev1 "cosmossdk.io/api/cosmos/distribution/module/v1" + genutilmodulev1 "cosmossdk.io/api/cosmos/genutil/module/v1" + mintmodulev1 "cosmossdk.io/api/cosmos/mint/module/v1" + paramsmodulev1 "cosmossdk.io/api/cosmos/params/module/v1" + slashingmodulev1 "cosmossdk.io/api/cosmos/slashing/module/v1" + stakingmodulev1 "cosmossdk.io/api/cosmos/staking/module/v1" + txmodulev1 "cosmossdk.io/api/cosmos/tx/module/v1" +) + +var AppConfig = appconfig.Compose(&appv1alpha1.Config{ + Modules: []*appv1alpha1.ModuleConfig{ + { + Name: "runtime", + Config: appconfig.WrapAny(&runtimev1alpha1.Module{ + AppName: "StakingApp", + BeginBlockers: []string{ + minttypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + genutiltypes.ModuleName, + slashingtypes.ModuleName, + paramstypes.ModuleName, + }, + EndBlockers: []string{ + stakingtypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + genutiltypes.ModuleName, + distrtypes.ModuleName, + minttypes.ModuleName, + slashingtypes.ModuleName, + paramstypes.ModuleName, + }, + InitGenesis: []string{ + authtypes.ModuleName, + banktypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + minttypes.ModuleName, + slashingtypes.ModuleName, + genutiltypes.ModuleName, + paramstypes.ModuleName, + }, + }), + }, + { + Name: authtypes.ModuleName, + Config: appconfig.WrapAny(&authmodulev1.Module{ + Bech32Prefix: "cosmos", + ModuleAccountPermissions: []*authmodulev1.ModuleAccountPermission{ + {Account: authtypes.FeeCollectorName}, + {Account: distrtypes.ModuleName}, + {Account: minttypes.ModuleName, Permissions: []string{authtypes.Minter}}, + {Account: stakingtypes.BondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, + {Account: stakingtypes.NotBondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, + }, + }), + }, + + { + Name: banktypes.ModuleName, + Config: appconfig.WrapAny(&bankmodulev1.Module{}), + }, + { + Name: stakingtypes.ModuleName, + Config: appconfig.WrapAny(&stakingmodulev1.Module{}), + }, + { + Name: slashingtypes.ModuleName, + Config: appconfig.WrapAny(&slashingmodulev1.Module{}), + }, + { + Name: paramstypes.ModuleName, + Config: appconfig.WrapAny(¶msmodulev1.Module{}), + }, + { + Name: "tx", + Config: appconfig.WrapAny(&txmodulev1.Module{}), + }, + { + Name: genutiltypes.ModuleName, + Config: appconfig.WrapAny(&genutilmodulev1.Module{}), + }, + { + Name: minttypes.ModuleName, + Config: appconfig.WrapAny(&mintmodulev1.Module{}), + }, + { + Name: distrtypes.ModuleName, + Config: appconfig.WrapAny(&distrmodulev1.Module{}), + }, + }, +}) diff --git a/x/staking/testutil/expected_keepers_mocks.go b/x/staking/testutil/expected_keepers_mocks.go new file mode 100644 index 0000000000000..e84885bb5209c --- /dev/null +++ b/x/staking/testutil/expected_keepers_mocks.go @@ -0,0 +1,697 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/staking/types/expected_keepers.go + +// Package testutil is a generated GoMock package. +package testutil + +import ( + reflect "reflect" + + math "cosmossdk.io/math" + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" + types1 "github.com/cosmos/cosmos-sdk/x/staking/types" + gomock "github.com/golang/mock/gomock" +) + +// MockDistributionKeeper is a mock of DistributionKeeper interface. +type MockDistributionKeeper struct { + ctrl *gomock.Controller + recorder *MockDistributionKeeperMockRecorder +} + +// MockDistributionKeeperMockRecorder is the mock recorder for MockDistributionKeeper. +type MockDistributionKeeperMockRecorder struct { + mock *MockDistributionKeeper +} + +// NewMockDistributionKeeper creates a new mock instance. +func NewMockDistributionKeeper(ctrl *gomock.Controller) *MockDistributionKeeper { + mock := &MockDistributionKeeper{ctrl: ctrl} + mock.recorder = &MockDistributionKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder { + return m.recorder +} + +// GetFeePoolCommunityCoins mocks base method. +func (m *MockDistributionKeeper) GetFeePoolCommunityCoins(ctx types.Context) types.DecCoins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeePoolCommunityCoins", ctx) + ret0, _ := ret[0].(types.DecCoins) + return ret0 +} + +// GetFeePoolCommunityCoins indicates an expected call of GetFeePoolCommunityCoins. +func (mr *MockDistributionKeeperMockRecorder) GetFeePoolCommunityCoins(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeePoolCommunityCoins", reflect.TypeOf((*MockDistributionKeeper)(nil).GetFeePoolCommunityCoins), ctx) +} + +// GetValidatorOutstandingRewardsCoins mocks base method. +func (m *MockDistributionKeeper) GetValidatorOutstandingRewardsCoins(ctx types.Context, val types.ValAddress) types.DecCoins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatorOutstandingRewardsCoins", ctx, val) + ret0, _ := ret[0].(types.DecCoins) + return ret0 +} + +// GetValidatorOutstandingRewardsCoins indicates an expected call of GetValidatorOutstandingRewardsCoins. +func (mr *MockDistributionKeeperMockRecorder) GetValidatorOutstandingRewardsCoins(ctx, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorOutstandingRewardsCoins", reflect.TypeOf((*MockDistributionKeeper)(nil).GetValidatorOutstandingRewardsCoins), ctx, val) +} + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetAccount mocks base method. +func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, addr) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) +} + +// GetModuleAccount mocks base method. +func (m *MockAccountKeeper) GetModuleAccount(ctx types.Context, moduleName string) types0.ModuleAccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName) + ret0, _ := ret[0].(types0.ModuleAccountI) + return ret0 +} + +// GetModuleAccount indicates an expected call of GetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName) +} + +// GetModuleAddress mocks base method. +func (m *MockAccountKeeper) GetModuleAddress(name string) types.AccAddress { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAddress", name) + ret0, _ := ret[0].(types.AccAddress) + return ret0 +} + +// GetModuleAddress indicates an expected call of GetModuleAddress. +func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name) +} + +// IterateAccounts mocks base method. +func (m *MockAccountKeeper) IterateAccounts(ctx types.Context, process func(types0.AccountI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateAccounts", ctx, process) +} + +// IterateAccounts indicates an expected call of IterateAccounts. +func (mr *MockAccountKeeperMockRecorder) IterateAccounts(ctx, process interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateAccounts", reflect.TypeOf((*MockAccountKeeper)(nil).IterateAccounts), ctx, process) +} + +// SetModuleAccount mocks base method. +func (m *MockAccountKeeper) SetModuleAccount(arg0 types.Context, arg1 types0.ModuleAccountI) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetModuleAccount", arg0, arg1) +} + +// SetModuleAccount indicates an expected call of SetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// BurnCoins mocks base method. +func (m *MockBankKeeper) BurnCoins(ctx types.Context, name string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BurnCoins", ctx, name, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// BurnCoins indicates an expected call of BurnCoins. +func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, name, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BurnCoins", reflect.TypeOf((*MockBankKeeper)(nil).BurnCoins), ctx, name, amt) +} + +// DelegateCoinsFromAccountToModule mocks base method. +func (m *MockBankKeeper) DelegateCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DelegateCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// DelegateCoinsFromAccountToModule indicates an expected call of DelegateCoinsFromAccountToModule. +func (mr *MockBankKeeperMockRecorder) DelegateCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelegateCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).DelegateCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt) +} + +// GetAllBalances mocks base method. +func (m *MockBankKeeper) GetAllBalances(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// GetAllBalances indicates an expected call of GetAllBalances. +func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr) +} + +// GetBalance mocks base method. +func (m *MockBankKeeper) GetBalance(ctx types.Context, addr types.AccAddress, denom string) types.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom) + ret0, _ := ret[0].(types.Coin) + return ret0 +} + +// GetBalance indicates an expected call of GetBalance. +func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom) +} + +// GetSupply mocks base method. +func (m *MockBankKeeper) GetSupply(ctx types.Context, denom string) types.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupply", ctx, denom) + ret0, _ := ret[0].(types.Coin) + return ret0 +} + +// GetSupply indicates an expected call of GetSupply. +func (mr *MockBankKeeperMockRecorder) GetSupply(ctx, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupply", reflect.TypeOf((*MockBankKeeper)(nil).GetSupply), ctx, denom) +} + +// LockedCoins mocks base method. +func (m *MockBankKeeper) LockedCoins(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LockedCoins", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// LockedCoins indicates an expected call of LockedCoins. +func (mr *MockBankKeeperMockRecorder) LockedCoins(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockedCoins", reflect.TypeOf((*MockBankKeeper)(nil).LockedCoins), ctx, addr) +} + +// SendCoinsFromModuleToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx types.Context, senderPool, recipientPool string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", ctx, senderPool, recipientPool, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToModule indicates an expected call of SendCoinsFromModuleToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderPool, recipientPool, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), ctx, senderPool, recipientPool, amt) +} + +// SpendableCoins mocks base method. +func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// SpendableCoins indicates an expected call of SpendableCoins. +func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) +} + +// UndelegateCoinsFromModuleToAccount mocks base method. +func (m *MockBankKeeper) UndelegateCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UndelegateCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// UndelegateCoinsFromModuleToAccount indicates an expected call of UndelegateCoinsFromModuleToAccount. +func (mr *MockBankKeeperMockRecorder) UndelegateCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UndelegateCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).UndelegateCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt) +} + +// MockValidatorSet is a mock of ValidatorSet interface. +type MockValidatorSet struct { + ctrl *gomock.Controller + recorder *MockValidatorSetMockRecorder +} + +// MockValidatorSetMockRecorder is the mock recorder for MockValidatorSet. +type MockValidatorSetMockRecorder struct { + mock *MockValidatorSet +} + +// NewMockValidatorSet creates a new mock instance. +func NewMockValidatorSet(ctrl *gomock.Controller) *MockValidatorSet { + mock := &MockValidatorSet{ctrl: ctrl} + mock.recorder = &MockValidatorSetMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockValidatorSet) EXPECT() *MockValidatorSetMockRecorder { + return m.recorder +} + +// Delegation mocks base method. +func (m *MockValidatorSet) Delegation(arg0 types.Context, arg1 types.AccAddress, arg2 types.ValAddress) types1.DelegationI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delegation", arg0, arg1, arg2) + ret0, _ := ret[0].(types1.DelegationI) + return ret0 +} + +// Delegation indicates an expected call of Delegation. +func (mr *MockValidatorSetMockRecorder) Delegation(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delegation", reflect.TypeOf((*MockValidatorSet)(nil).Delegation), arg0, arg1, arg2) +} + +// IterateBondedValidatorsByPower mocks base method. +func (m *MockValidatorSet) IterateBondedValidatorsByPower(arg0 types.Context, arg1 func(int64, types1.ValidatorI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateBondedValidatorsByPower", arg0, arg1) +} + +// IterateBondedValidatorsByPower indicates an expected call of IterateBondedValidatorsByPower. +func (mr *MockValidatorSetMockRecorder) IterateBondedValidatorsByPower(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateBondedValidatorsByPower", reflect.TypeOf((*MockValidatorSet)(nil).IterateBondedValidatorsByPower), arg0, arg1) +} + +// IterateLastValidators mocks base method. +func (m *MockValidatorSet) IterateLastValidators(arg0 types.Context, arg1 func(int64, types1.ValidatorI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateLastValidators", arg0, arg1) +} + +// IterateLastValidators indicates an expected call of IterateLastValidators. +func (mr *MockValidatorSetMockRecorder) IterateLastValidators(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateLastValidators", reflect.TypeOf((*MockValidatorSet)(nil).IterateLastValidators), arg0, arg1) +} + +// IterateValidators mocks base method. +func (m *MockValidatorSet) IterateValidators(arg0 types.Context, arg1 func(int64, types1.ValidatorI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateValidators", arg0, arg1) +} + +// IterateValidators indicates an expected call of IterateValidators. +func (mr *MockValidatorSetMockRecorder) IterateValidators(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateValidators", reflect.TypeOf((*MockValidatorSet)(nil).IterateValidators), arg0, arg1) +} + +// Jail mocks base method. +func (m *MockValidatorSet) Jail(arg0 types.Context, arg1 types.ConsAddress) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Jail", arg0, arg1) +} + +// Jail indicates an expected call of Jail. +func (mr *MockValidatorSetMockRecorder) Jail(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Jail", reflect.TypeOf((*MockValidatorSet)(nil).Jail), arg0, arg1) +} + +// MaxValidators mocks base method. +func (m *MockValidatorSet) MaxValidators(arg0 types.Context) uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MaxValidators", arg0) + ret0, _ := ret[0].(uint32) + return ret0 +} + +// MaxValidators indicates an expected call of MaxValidators. +func (mr *MockValidatorSetMockRecorder) MaxValidators(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxValidators", reflect.TypeOf((*MockValidatorSet)(nil).MaxValidators), arg0) +} + +// Slash mocks base method. +func (m *MockValidatorSet) Slash(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 types.Dec) math.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Slash", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(math.Int) + return ret0 +} + +// Slash indicates an expected call of Slash. +func (mr *MockValidatorSetMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Slash", reflect.TypeOf((*MockValidatorSet)(nil).Slash), arg0, arg1, arg2, arg3, arg4) +} + +// StakingTokenSupply mocks base method. +func (m *MockValidatorSet) StakingTokenSupply(arg0 types.Context) math.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StakingTokenSupply", arg0) + ret0, _ := ret[0].(math.Int) + return ret0 +} + +// StakingTokenSupply indicates an expected call of StakingTokenSupply. +func (mr *MockValidatorSetMockRecorder) StakingTokenSupply(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockValidatorSet)(nil).StakingTokenSupply), arg0) +} + +// TotalBondedTokens mocks base method. +func (m *MockValidatorSet) TotalBondedTokens(arg0 types.Context) math.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalBondedTokens", arg0) + ret0, _ := ret[0].(math.Int) + return ret0 +} + +// TotalBondedTokens indicates an expected call of TotalBondedTokens. +func (mr *MockValidatorSetMockRecorder) TotalBondedTokens(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockValidatorSet)(nil).TotalBondedTokens), arg0) +} + +// Unjail mocks base method. +func (m *MockValidatorSet) Unjail(arg0 types.Context, arg1 types.ConsAddress) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Unjail", arg0, arg1) +} + +// Unjail indicates an expected call of Unjail. +func (mr *MockValidatorSetMockRecorder) Unjail(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unjail", reflect.TypeOf((*MockValidatorSet)(nil).Unjail), arg0, arg1) +} + +// Validator mocks base method. +func (m *MockValidatorSet) Validator(arg0 types.Context, arg1 types.ValAddress) types1.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Validator", arg0, arg1) + ret0, _ := ret[0].(types1.ValidatorI) + return ret0 +} + +// Validator indicates an expected call of Validator. +func (mr *MockValidatorSetMockRecorder) Validator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockValidatorSet)(nil).Validator), arg0, arg1) +} + +// ValidatorByConsAddr mocks base method. +func (m *MockValidatorSet) ValidatorByConsAddr(arg0 types.Context, arg1 types.ConsAddress) types1.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatorByConsAddr", arg0, arg1) + ret0, _ := ret[0].(types1.ValidatorI) + return ret0 +} + +// ValidatorByConsAddr indicates an expected call of ValidatorByConsAddr. +func (mr *MockValidatorSetMockRecorder) ValidatorByConsAddr(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorByConsAddr", reflect.TypeOf((*MockValidatorSet)(nil).ValidatorByConsAddr), arg0, arg1) +} + +// MockDelegationSet is a mock of DelegationSet interface. +type MockDelegationSet struct { + ctrl *gomock.Controller + recorder *MockDelegationSetMockRecorder +} + +// MockDelegationSetMockRecorder is the mock recorder for MockDelegationSet. +type MockDelegationSetMockRecorder struct { + mock *MockDelegationSet +} + +// NewMockDelegationSet creates a new mock instance. +func NewMockDelegationSet(ctrl *gomock.Controller) *MockDelegationSet { + mock := &MockDelegationSet{ctrl: ctrl} + mock.recorder = &MockDelegationSetMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDelegationSet) EXPECT() *MockDelegationSetMockRecorder { + return m.recorder +} + +// GetValidatorSet mocks base method. +func (m *MockDelegationSet) GetValidatorSet() types1.ValidatorSet { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatorSet") + ret0, _ := ret[0].(types1.ValidatorSet) + return ret0 +} + +// GetValidatorSet indicates an expected call of GetValidatorSet. +func (mr *MockDelegationSetMockRecorder) GetValidatorSet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorSet", reflect.TypeOf((*MockDelegationSet)(nil).GetValidatorSet)) +} + +// IterateDelegations mocks base method. +func (m *MockDelegationSet) IterateDelegations(ctx types.Context, delegator types.AccAddress, fn func(int64, types1.DelegationI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateDelegations", ctx, delegator, fn) +} + +// IterateDelegations indicates an expected call of IterateDelegations. +func (mr *MockDelegationSetMockRecorder) IterateDelegations(ctx, delegator, fn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateDelegations", reflect.TypeOf((*MockDelegationSet)(nil).IterateDelegations), ctx, delegator, fn) +} + +// MockStakingHooks is a mock of StakingHooks interface. +type MockStakingHooks struct { + ctrl *gomock.Controller + recorder *MockStakingHooksMockRecorder +} + +// MockStakingHooksMockRecorder is the mock recorder for MockStakingHooks. +type MockStakingHooksMockRecorder struct { + mock *MockStakingHooks +} + +// NewMockStakingHooks creates a new mock instance. +func NewMockStakingHooks(ctrl *gomock.Controller) *MockStakingHooks { + mock := &MockStakingHooks{ctrl: ctrl} + mock.recorder = &MockStakingHooksMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingHooks) EXPECT() *MockStakingHooksMockRecorder { + return m.recorder +} + +// AfterDelegationModified mocks base method. +func (m *MockStakingHooks) AfterDelegationModified(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterDelegationModified", ctx, delAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// AfterDelegationModified indicates an expected call of AfterDelegationModified. +func (mr *MockStakingHooksMockRecorder) AfterDelegationModified(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterDelegationModified", reflect.TypeOf((*MockStakingHooks)(nil).AfterDelegationModified), ctx, delAddr, valAddr) +} + +// AfterValidatorBeginUnbonding mocks base method. +func (m *MockStakingHooks) AfterValidatorBeginUnbonding(ctx types.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterValidatorBeginUnbonding", ctx, consAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// AfterValidatorBeginUnbonding indicates an expected call of AfterValidatorBeginUnbonding. +func (mr *MockStakingHooksMockRecorder) AfterValidatorBeginUnbonding(ctx, consAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterValidatorBeginUnbonding", reflect.TypeOf((*MockStakingHooks)(nil).AfterValidatorBeginUnbonding), ctx, consAddr, valAddr) +} + +// AfterValidatorBonded mocks base method. +func (m *MockStakingHooks) AfterValidatorBonded(ctx types.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterValidatorBonded", ctx, consAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// AfterValidatorBonded indicates an expected call of AfterValidatorBonded. +func (mr *MockStakingHooksMockRecorder) AfterValidatorBonded(ctx, consAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterValidatorBonded", reflect.TypeOf((*MockStakingHooks)(nil).AfterValidatorBonded), ctx, consAddr, valAddr) +} + +// AfterValidatorCreated mocks base method. +func (m *MockStakingHooks) AfterValidatorCreated(ctx types.Context, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterValidatorCreated", ctx, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// AfterValidatorCreated indicates an expected call of AfterValidatorCreated. +func (mr *MockStakingHooksMockRecorder) AfterValidatorCreated(ctx, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterValidatorCreated", reflect.TypeOf((*MockStakingHooks)(nil).AfterValidatorCreated), ctx, valAddr) +} + +// AfterValidatorRemoved mocks base method. +func (m *MockStakingHooks) AfterValidatorRemoved(ctx types.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterValidatorRemoved", ctx, consAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// AfterValidatorRemoved indicates an expected call of AfterValidatorRemoved. +func (mr *MockStakingHooksMockRecorder) AfterValidatorRemoved(ctx, consAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterValidatorRemoved", reflect.TypeOf((*MockStakingHooks)(nil).AfterValidatorRemoved), ctx, consAddr, valAddr) +} + +// BeforeDelegationCreated mocks base method. +func (m *MockStakingHooks) BeforeDelegationCreated(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeDelegationCreated", ctx, delAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforeDelegationCreated indicates an expected call of BeforeDelegationCreated. +func (mr *MockStakingHooksMockRecorder) BeforeDelegationCreated(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeDelegationCreated", reflect.TypeOf((*MockStakingHooks)(nil).BeforeDelegationCreated), ctx, delAddr, valAddr) +} + +// BeforeDelegationRemoved mocks base method. +func (m *MockStakingHooks) BeforeDelegationRemoved(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeDelegationRemoved", ctx, delAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforeDelegationRemoved indicates an expected call of BeforeDelegationRemoved. +func (mr *MockStakingHooksMockRecorder) BeforeDelegationRemoved(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeDelegationRemoved", reflect.TypeOf((*MockStakingHooks)(nil).BeforeDelegationRemoved), ctx, delAddr, valAddr) +} + +// BeforeDelegationSharesModified mocks base method. +func (m *MockStakingHooks) BeforeDelegationSharesModified(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeDelegationSharesModified", ctx, delAddr, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforeDelegationSharesModified indicates an expected call of BeforeDelegationSharesModified. +func (mr *MockStakingHooksMockRecorder) BeforeDelegationSharesModified(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeDelegationSharesModified", reflect.TypeOf((*MockStakingHooks)(nil).BeforeDelegationSharesModified), ctx, delAddr, valAddr) +} + +// BeforeValidatorModified mocks base method. +func (m *MockStakingHooks) BeforeValidatorModified(ctx types.Context, valAddr types.ValAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeValidatorModified", ctx, valAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforeValidatorModified indicates an expected call of BeforeValidatorModified. +func (mr *MockStakingHooksMockRecorder) BeforeValidatorModified(ctx, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeValidatorModified", reflect.TypeOf((*MockStakingHooks)(nil).BeforeValidatorModified), ctx, valAddr) +} + +// BeforeValidatorSlashed mocks base method. +func (m *MockStakingHooks) BeforeValidatorSlashed(ctx types.Context, valAddr types.ValAddress, fraction types.Dec) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeValidatorSlashed", ctx, valAddr, fraction) + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforeValidatorSlashed indicates an expected call of BeforeValidatorSlashed. +func (mr *MockStakingHooksMockRecorder) BeforeValidatorSlashed(ctx, valAddr, fraction interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeValidatorSlashed", reflect.TypeOf((*MockStakingHooks)(nil).BeforeValidatorSlashed), ctx, valAddr, fraction) +}