From 1cb7acdb8c801e8ffdee24cc3c370131fe4cefa8 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 20 Sep 2022 06:23:21 -0400 Subject: [PATCH 1/4] fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323) (cherry picked from commit c1c23a75d932a31acc907d896f0fb66194bdbc18) # Conflicts: # CHANGELOG.md # tests/integration/distribution/keeper/delegation_test.go # testutil/sims/app_helpers.go # x/distribution/keeper/delegation.go # x/distribution/keeper/delegation_test.go # x/distribution/keeper/keeper.go --- CHANGELOG.md | 75 ++ .../distribution/keeper/delegation_test.go | 820 ++++++++++++++++++ testutil/sims/app_helpers.go | 287 ++++++ x/distribution/keeper/delegation.go | 34 +- x/distribution/keeper/delegation_test.go | 98 +++ x/distribution/keeper/keeper.go | 3 + 6 files changed, 1312 insertions(+), 5 deletions(-) create mode 100644 tests/integration/distribution/keeper/delegation_test.go create mode 100644 testutil/sims/app_helpers.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ba18ad9613..ac9216f17461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,81 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +<<<<<<< HEAD +======= +### Features + +* (cli) [#13304](https://github.com/cosmos/cosmos-sdk/pull/13304) Add `tx gov draft-proposal` command for generating proposal JSONs. +* (cli) [#13207](https://github.com/cosmos/cosmos-sdk/pull/13207) Reduce user's password prompts when calling keyring `List()` function +* (x/authz) [#12648](https://github.com/cosmos/cosmos-sdk/pull/12648) Add an allow list, an optional list of addresses allowed to receive bank assets via authz MsgSend grant. +* (sdk.Coins) [#12627](https://github.com/cosmos/cosmos-sdk/pull/12627) Make a Denoms method on sdk.Coins. +* (testutil) [#12973](https://github.com/cosmos/cosmos-sdk/pull/12973) Add generic `testutil.RandSliceElem` function which selects a random element from the list. +* (client) [#12936](https://github.com/cosmos/cosmos-sdk/pull/12936) Add capability to preprocess transactions before broadcasting from a higher level chain. +* (x/authz) [#13047](https://github.com/cosmos/cosmos-sdk/pull/13047) Add a GetAuthorization function to the keeper. +* (cli) [#13064](https://github.com/cosmos/cosmos-sdk/pull/13064) Add `debug prefixes` to list supported HRP prefixes via . +* (cli) [#12742](https://github.com/cosmos/cosmos-sdk/pull/12742) Add the `prune` CLI cmd to manually prune app store history versions based on the pruning options. +* (ledger) [#12935](https://github.com/cosmos/cosmos-sdk/pull/12935) Generalize Ledger integration to allow for different apps or keytypes that use SECP256k1. +* (x/bank) [#11981](https://github.com/cosmos/cosmos-sdk/pull/11981) Create the `SetSendEnabled` endpoint for managing the bank's SendEnabled settings. +* (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info. + +### Improvements + +* [#13323](https://github.com/cosmos/cosmos-sdk/pull/13323) Ensure `withdraw_rewards` rewards are emitted from all actions that result in rewards being withdrawn. +* [#13214](https://github.com/cosmos/cosmos-sdk/pull/13214) Add `withdraw-proposal` command to group module's CLI transaction commands. +* [#13070](https://github.com/cosmos/cosmos-sdk/pull/13070) Migrate from `gogo/protobuf` to `cosmos/gogoproto`. +* [#12981](https://github.com/cosmos/cosmos-sdk/pull/12981) Return proper error when parsing telemetry configuration. +* [#12995](https://github.com/cosmos/cosmos-sdk/pull/12995) Add `FormatTime` and `ParseTimeString` methods. +* [#12952](https://github.com/cosmos/cosmos-sdk/pull/12952) Replace keyring module to Cosmos fork. +* [#12352](https://github.com/cosmos/cosmos-sdk/pull/12352) Move the `RegisterSwaggerAPI` logic into a separate helper function in the server package. +* [#12876](https://github.com/cosmos/cosmos-sdk/pull/12876) Remove proposer-based rewards. +* [#12892](https://github.com/cosmos/cosmos-sdk/pull/12892) `make format` now runs only gofumpt and golangci-lint run ./... --fix, replacing `goimports` `gofmt` and `misspell` +* [#12846](https://github.com/cosmos/cosmos-sdk/pull/12846) Remove `RandomizedParams` from the `AppModuleSimulation` interface which is no longer needed. +* (events) [#12850](https://github.com/cosmos/cosmos-sdk/pull/12850) Add a new `fee_payer` attribute to the `tx` event that is emitted from the `DeductFeeDecorator` AnteHandler decorator. +* (ci) [#12854](https://github.com/cosmos/cosmos-sdk/pull/12854) Use ghcr.io to host the proto builder image. Update proto builder image to go 1.19 +* (x/bank) [#12706](https://github.com/cosmos/cosmos-sdk/pull/12706) Added the `chain-id` flag to the `AddTxFlagsToCmd` API. There is no longer a need to explicitly register this flag on commands whens `AddTxFlagsToCmd` is already called. +* [#12791](https://github.com/cosmos/cosmos-sdk/pull/12791) Bump the math library used in the sdk and replace old usages of sdk.\* +* (x/params) [#12615](https://github.com/cosmos/cosmos-sdk/pull/12615) Add `GetParamSetIfExists` function to params `Subspace` to prevent panics on breaking changes. +* [#12717](https://github.com/cosmos/cosmos-sdk/pull/12717) Use injected encoding params in simapp. +* (x/bank) [#12674](https://github.com/cosmos/cosmos-sdk/pull/12674) Add convenience function `CreatePrefixedAccountStoreKey()` to construct key to access account's balance for a given denom. +* [#12702](https://github.com/cosmos/cosmos-sdk/pull/12702) Linting and tidiness, fixed two minor security warnings. +* [#12634](https://github.com/cosmos/cosmos-sdk/pull/12634) Move `sdk.Dec` to math package. +* [#12596](https://github.com/cosmos/cosmos-sdk/pull/12596) Remove all imports of the non-existent gogo/protobuf v1.3.3 to ease downstream use and go workspaces. +* [#12187](https://github.com/cosmos/cosmos-sdk/pull/12187) Add batch operation for x/nft module. +* [#12693](https://github.com/cosmos/cosmos-sdk/pull/12693) Make sure the order of each node is consistent when emitting proto events. +* [#12455](https://github.com/cosmos/cosmos-sdk/pull/12455) Show attempts count in error for signing. +* [#12886](https://github.com/cosmos/cosmos-sdk/pull/12886) Amortize cost of processing cache KV store +* [#12953](https://github.com/cosmos/cosmos-sdk/pull/12953) Change the default priority mechanism to be based on gas price. +* [#13048](https://github.com/cosmos/cosmos-sdk/pull/13048) Add handling of AccountNumberStoreKeyPrefix to the x/auth simulation decoder. +* [#13101](https://github.com/cosmos/cosmos-sdk/pull/13101) Remove weights from `simapp/params` and `testutil/sims`. They are now in their respective modules. +* (simapp) [#13107](https://github.com/cosmos/cosmos-sdk/pull/13107) Call `SetIAVLCacheSize` with the configured value in simapp. +* [#12398](https://github.com/cosmos/cosmos-sdk/issues/12398) Refactor all `x` modules to unit-test via mocks and decouple `simapp`. +* [#13144](https://github.com/cosmos/cosmos-sdk/pull/13144) Add validator distribution info grpc gateway get endpoint. +* [#13168](https://github.com/cosmos/cosmos-sdk/pull/13168) Migrate tendermintdev/proto-builder to ghcr.io. New image `ghcr.io/cosmos/proto-builder:0.8` +* [#13178](https://github.com/cosmos/cosmos-sdk/pull/13178) Add `cosmos.msg.v1.service` protobuf annotation to allow tooling to distinguish between Msg and Query services via reflection. +* [#13233](https://github.com/cosmos/cosmos-sdk/pull/13233) Add `--append` to `add-genesis-account` sub-command to append new tokens after an account is already created. +* [#13236](https://github.com/cosmos/cosmos-sdk/pull/13236) Integrate Filter Logging +* [#13301](https://github.com/cosmos/cosmos-sdk/pull/13301) Keep the balance query endpoint compatible with legacy blocks +* [#13321](https://github.com/cosmos/cosmos-sdk/pull/13321) Add flag to disable fast node migration and usage. + +### State Machine Breaking + +* (codec) [#13307](https://github.com/cosmos/cosmos-sdk/pull/13307) Register all modules' `Msg`s with group's ModuleCdc so that Amino sign bytes are correctly generated. +* (codec) [#13196](https://github.com/cosmos/cosmos-sdk/pull/13196) Register all modules' `Msg`s with gov's ModuleCdc so that Amino sign bytes are correctly generated. +* (x/distribution) [#12852](https://github.com/cosmos/cosmos-sdk/pull/12852) Deprecate `CommunityPoolSpendProposal`. Please execute a `MsgCommunityPoolSpend` message via the new v1 `x/gov` module instead. This message can be used to directly fund the `x/gov` module account. +* (x/bank) [#12610](https://github.com/cosmos/cosmos-sdk/pull/12610) `MsgMultiSend` now allows only a single input. +* (x/bank) [#12630](https://github.com/cosmos/cosmos-sdk/pull/12630) Migrate `x/bank` to self-managed parameters and deprecate its usage of `x/params`. +* (x/auth) [#12475](https://github.com/cosmos/cosmos-sdk/pull/12475) Migrate `x/auth` to self-managed parameters and deprecate its usage of `x/params`. +* (x/slashing) [#12399](https://github.com/cosmos/cosmos-sdk/pull/12399) Migrate `x/slashing` to self-managed parameters and deprecate its usage of `x/params`. +* (x/mint) [#12363](https://github.com/cosmos/cosmos-sdk/pull/12363) Migrate `x/mint` to self-managed parameters and deprecate it's usage of `x/params`. +* (x/distribution) [#12434](https://github.com/cosmos/cosmos-sdk/pull/12434) Migrate `x/distribution` to self-managed parameters and deprecate it's usage of `x/params`. +* (x/crisis) [#12445](https://github.com/cosmos/cosmos-sdk/pull/12445) Migrate `x/crisis` to self-managed parameters and deprecate it's usage of `x/params`. +* (x/gov) [#12631](https://github.com/cosmos/cosmos-sdk/pull/12631) Migrate `x/gov` to self-managed parameters and deprecate it's usage of `x/params`. +* (x/staking) [#12409](https://github.com/cosmos/cosmos-sdk/pull/12409) Migrate `x/staking` to self-managed parameters and deprecate it's usage of `x/params`. +* (x/bank) [#11859](https://github.com/cosmos/cosmos-sdk/pull/11859) Move the SendEnabled information out of the Params and into the state store directly. +* (x/gov) [#12771](https://github.com/cosmos/cosmos-sdk/pull/12771) Initial deposit requirement for proposals at submission time. +* (x/staking) [#12967](https://github.com/cosmos/cosmos-sdk/pull/12967) `unbond` now creates only one unbonding delegation entry when multiple unbondings exist at a single height (e.g. through multiple messages in a transaction). + +>>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) ### API Breaking Changes - (cli) [#13089](https://github.com/cosmos/cosmos-sdk/pull/13089) Fix rollback command don't actually delete multistore versions, added method `RollbackToVersion` to interface `CommitMultiStore` and added method `CommitMultiStore` to `Application` interface. diff --git a/tests/integration/distribution/keeper/delegation_test.go b/tests/integration/distribution/keeper/delegation_test.go new file mode 100644 index 000000000000..0bb991158114 --- /dev/null +++ b/tests/integration/distribution/keeper/delegation_test.go @@ -0,0 +1,820 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestCalculateRewardsBasic(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator and start new block + staking.EndBlocker(ctx, stakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + tstaking.Ctx = ctx + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // historical count should be 2 (once for validator init, once for delegation init) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // historical count should be 2 still + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // allocate some rewards + initial := int64(10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsAfterSlash(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valPower := int64(100) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // retrieve validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsAfterManySlashes(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator by 50% again + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegator(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + initial := int64(20) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // second delegation + tstaking.Ctx = ctx + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 3/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 3 / 4)}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 1 / 4)}}, rewards) + + // commission should be equal to initial (50% twice) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestWithdrawDelegationRewardsBasic(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + balancePower := int64(1000) + balanceTokens := stakingKeeper.TokensFromConsensusPower(ctx, balancePower) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + // create validator with 50% commission + power := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valTokens := tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, power, true) + + // assert correct initial balance + expTokens := balanceTokens.Sub(valTokens) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, expTokens)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)} + + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // historical count should be 2 (initial + latest for delegation) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // withdraw rewards + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.Nil(t, err) + + // historical count should still be 2 (added one record, cleared one) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // assert correct balance + exp := balanceTokens.Sub(valTokens).Add(initial.QuoRaw(2)) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) + + // withdraw commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.Nil(t, err) + + // assert correct balance + exp = balanceTokens.Sub(valTokens).Add(initial) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) +} + +func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 10)) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // slash the validator by 50% again + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valPower := int64(100) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 30)) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // second delegation + tstaking.DelegateWithPower(sdk.AccAddress(valAddrs[1]), valAddrs[0], 100) + + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator again + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be initial / 3 + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards) + + // commission should be equal to initial (twice 50% commission, unaffected by slashing) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + initial := int64(20) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // historical count should be 2 (validator init, delegation init) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // second delegation + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + + // historical count should be 3 (second delegation init) + require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // first delegator withdraws + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + // second delegator withdraws + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + require.NoError(t, err) + + // historical count should be 3 (validator init + two delegations) + require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // validator withdraws commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be zero + require.True(t, rewards.IsZero()) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be zero + require.True(t, rewards.IsZero()) + + // commission should be zero + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // first delegator withdraws again + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be zero + require.True(t, rewards.IsZero()) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) + + // commission should be half initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // withdraw commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/2 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) + + // commission should be zero + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) +} + +func Test100PercentCommissionReward(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + initial := int64(20) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + + // create validator with 100% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator + val := stakingKeeper.Validator(ctx, valAddrs[0]) + + // allocate some rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())} + require.True(t, rewards.IsEqual(zeroRewards)) + + events := ctx.EventManager().Events() + lastEvent := events[len(events)-1] + + var hasValue bool + for _, attr := range lastEvent.Attributes { + if attr.Key == "amount" && attr.Value == "0stake" { + hasValue = true + } + } + require.True(t, hasValue) +} diff --git a/testutil/sims/app_helpers.go b/testutil/sims/app_helpers.go new file mode 100644 index 000000000000..08aa4597e15a --- /dev/null +++ b/testutil/sims/app_helpers.go @@ -0,0 +1,287 @@ +package sims + +import ( + "encoding/json" + "fmt" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "cosmossdk.io/depinject" + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/runtime" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil/mock" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// SimAppChainID hardcoded chainID for simulation +const ( + DefaultGenTxGas = 10000000 + SimAppChainID = "simulation-app" +) + +// DefaultConsensusParams defines the default Tendermint consensus params used in +// SimApp testing. +var DefaultConsensusParams = &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxBytes: 200000, + MaxGas: 2000000, + }, + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: 302400, + MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration + MaxBytes: 10000, + }, + Validator: &tmproto.ValidatorParams{ + PubKeyTypes: []string{ + tmtypes.ABCIPubKeyTypeEd25519, + }, + }, +} + +// CreateRandomValidatorSet creates a validator set with one random validator +func CreateRandomValidatorSet() (*tmtypes.ValidatorSet, error) { + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + if err != nil { + return nil, fmt.Errorf("failed to get pub key: %w", err) + } + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + + return tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}), nil +} + +type GenesisAccount struct { + authtypes.GenesisAccount + Coins sdk.Coins +} + +// StartupConfig defines the startup configuration new a test application. +// +// ValidatorSet defines a custom validator set to be validating the app. +// BaseAppOption defines the additional operations that must be run on baseapp before app start. +// AtGenesis defines if the app started should already have produced block or not. +type StartupConfig struct { + ValidatorSet func() (*tmtypes.ValidatorSet, error) + BaseAppOption runtime.BaseAppOption + AtGenesis bool + GenesisAccounts []GenesisAccount +} + +func DefaultStartUpConfig() StartupConfig { + priv := secp256k1.GenPrivKey() + ba := authtypes.NewBaseAccount(priv.PubKey().Address().Bytes(), priv.PubKey(), 0, 0) + ga := GenesisAccount{ba, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000)))} + return StartupConfig{ + ValidatorSet: CreateRandomValidatorSet, + AtGenesis: false, + GenesisAccounts: []GenesisAccount{ga}, + } +} + +// Setup initializes a new runtime.App and can inject values into extraOutputs. +// It uses SetupWithConfiguration under the hood. +func Setup(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { + return SetupWithConfiguration(appConfig, DefaultStartUpConfig(), extraOutputs...) +} + +// SetupAtGenesis initializes a new runtime.App at genesis and can inject values into extraOutputs. +// It uses SetupWithConfiguration under the hood. +func SetupAtGenesis(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { + cfg := DefaultStartUpConfig() + cfg.AtGenesis = true + return SetupWithConfiguration(appConfig, cfg, extraOutputs...) +} + +// SetupWithConfiguration initializes a new runtime.App. A Nop logger is set in runtime.App. +// appConfig usually load from a `app.yaml` with `appconfig.LoadYAML`, defines the application configuration. +// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). +func SetupWithConfiguration(appConfig depinject.Config, startupConfig StartupConfig, extraOutputs ...interface{}) (*runtime.App, error) { + // create the app with depinject + var ( + app *runtime.App + appBuilder *runtime.AppBuilder + codec codec.Codec + ) + + if err := depinject.Inject( + appConfig, + append(extraOutputs, &appBuilder, &codec)..., + ); err != nil { + return nil, fmt.Errorf("failed to inject dependencies: %w", err) + } + + if startupConfig.BaseAppOption != nil { + app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil, startupConfig.BaseAppOption) + } else { + app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil) + } + if err := app.Load(true); err != nil { + return nil, fmt.Errorf("failed to load app: %w", err) + } + + // create validator set + valSet, err := startupConfig.ValidatorSet() + if err != nil { + return nil, fmt.Errorf("failed to create validator set") + } + + var ( + balances []banktypes.Balance + genAccounts []authtypes.GenesisAccount + ) + for _, ga := range startupConfig.GenesisAccounts { + genAccounts = append(genAccounts, ga.GenesisAccount) + balances = append(balances, banktypes.Balance{Address: ga.GenesisAccount.GetAddress().String(), Coins: ga.Coins}) + } + + genesisState, err := GenesisStateWithValSet(codec, appBuilder.DefaultGenesis(), valSet, genAccounts, balances...) + if err != nil { + return nil, fmt.Errorf("failed to create genesis state: %w", err) + } + + // init chain must be called to stop deliverState from being nil + stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") + if err != nil { + return nil, fmt.Errorf("failed to marshal default genesis state: %w", err) + } + + // init chain will set the validator set and initialize the genesis accounts + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + + // commit genesis changes + if !startupConfig.AtGenesis { + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }}) + } + + return app, nil +} + +// GenesisStateWithValSet returns a new genesis state with the validator set +func GenesisStateWithValSet( + codec codec.Codec, + genesisState map[string]json.RawMessage, + valSet *tmtypes.ValidatorSet, + genAccs []authtypes.GenesisAccount, + balances ...banktypes.Balance, +) (map[string]json.RawMessage, error) { + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis) + + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.DefaultPowerReduction + + for _, val := range valSet.Validators { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) + if err != nil { + return nil, fmt.Errorf("failed to convert pubkey: %w", err) + } + + pkAny, err := codectypes.NewAnyWithValue(pk) + if err != nil { + return nil, fmt.Errorf("failed to create new any: %w", err) + } + + validator := stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondAmt, + DelegatorShares: math.LegacyOneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), + MinSelfDelegation: math.ZeroInt(), + } + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), math.LegacyOneDec())) + + } + + // set validators and delegations + stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(stakingGenesis) + + totalSupply := sdk.NewCoins() + for _, b := range balances { + // add genesis acc tokens to total supply + totalSupply = totalSupply.Add(b.Coins...) + } + + for range delegations { + // add delegated tokens to total supply + totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) + } + + // add bonded amount to bonded pool module account + balances = append(balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + }) + + // update total supply + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) + genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis) + + return genesisState, nil +} + +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} + +// AppOptionsMap is a stub implementing AppOptions which can get data from a map +type AppOptionsMap map[string]interface{} + +func (m AppOptionsMap) Get(key string) interface{} { + v, ok := m[key] + if !ok { + return interface{}(nil) + } + + return v +} + +func NewAppOptionsWithFlagHome(homePath string) servertypes.AppOptions { + return AppOptionsMap{ + flags.FlagHome: homePath, + } +} diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 3b158c15585e..4df0ff10d158 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -3,8 +3,13 @@ package keeper import ( "fmt" +<<<<<<< HEAD sdk "github.com/cosmos/cosmos-sdk/types" +======= + "cosmossdk.io/math" +>>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -162,13 +167,13 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali ) } - // truncate coins, return remainder to community pool - coins, remainder := rewards.TruncateDecimal() + // truncate reward dec coins, return remainder to community pool + finalRewards, remainder := rewards.TruncateDecimal() // add coins to user account - if !coins.IsZero() { + if !finalRewards.IsZero() { withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr()) - err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, finalRewards) if err != nil { return nil, err } @@ -189,5 +194,24 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali // remove delegator starting info k.DeleteDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) - return coins, nil + if finalRewards.IsZero() { + baseDenom, _ := sdk.GetBaseDenom() + if baseDenom == "" { + baseDenom = sdk.DefaultBondDenom + } + + // Note, we do not call the NewCoins constructor as we do not want the zero + // coin removed. + finalRewards = sdk.Coins{sdk.NewCoin(baseDenom, math.ZeroInt())} + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeWithdrawRewards, + sdk.NewAttribute(sdk.AttributeKeyAmount, finalRewards.String()), + sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), + ), + ) + + return finalRewards, nil } diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 8b4219e28028..7b2632604a38 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -6,7 +6,13 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +<<<<<<< HEAD "github.com/cosmos/cosmos-sdk/simapp" +======= + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/testutil" +>>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" @@ -623,5 +629,97 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, rewards) // commission should be zero +<<<<<<< HEAD require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) +======= + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission.IsZero()) +} + +func Test100PercentCommissionReward(t *testing.T) { + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), + ) + + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) + + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, sdk.NewInt(100)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) + + // validator and delegation mocks + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del).Times(3) + + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(2) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some rewards + initial := int64(20) + tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(addr), valAddr) + require.NoError(t, err) + + zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())} + require.True(t, rewards.IsEqual(zeroRewards)) + + events := ctx.EventManager().Events() + lastEvent := events[len(events)-1] + + var hasValue bool + for _, attr := range lastEvent.Attributes { + if attr.Key == "amount" && attr.Value == "0stake" { + hasValue = true + } + } + require.True(t, hasValue) +>>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 894cd57ce2b5..8b180cfba362 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -98,6 +98,7 @@ func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddres return nil, err } +<<<<<<< HEAD ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeWithdrawRewards, @@ -106,6 +107,8 @@ func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddres ), ) +======= +>>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) // reinitialize the delegation k.initializeDelegation(ctx, valAddr, delAddr) return rewards, nil From bac27127f993d1b9c9b66f66f10911e3cdf3f572 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 20 Sep 2022 13:08:03 +0200 Subject: [PATCH 2/4] fix conflicts --- CHANGELOG.md | 71 -- .../distribution/keeper/delegation_test.go | 820 ------------------ testutil/sims/app_helpers.go | 287 ------ x/distribution/keeper/delegation.go | 8 +- x/distribution/keeper/delegation_test.go | 98 --- x/distribution/keeper/keeper.go | 11 - 6 files changed, 1 insertion(+), 1294 deletions(-) delete mode 100644 tests/integration/distribution/keeper/delegation_test.go delete mode 100644 testutil/sims/app_helpers.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ac9216f17461..341fb08235b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,81 +37,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] -<<<<<<< HEAD -======= -### Features - -* (cli) [#13304](https://github.com/cosmos/cosmos-sdk/pull/13304) Add `tx gov draft-proposal` command for generating proposal JSONs. -* (cli) [#13207](https://github.com/cosmos/cosmos-sdk/pull/13207) Reduce user's password prompts when calling keyring `List()` function -* (x/authz) [#12648](https://github.com/cosmos/cosmos-sdk/pull/12648) Add an allow list, an optional list of addresses allowed to receive bank assets via authz MsgSend grant. -* (sdk.Coins) [#12627](https://github.com/cosmos/cosmos-sdk/pull/12627) Make a Denoms method on sdk.Coins. -* (testutil) [#12973](https://github.com/cosmos/cosmos-sdk/pull/12973) Add generic `testutil.RandSliceElem` function which selects a random element from the list. -* (client) [#12936](https://github.com/cosmos/cosmos-sdk/pull/12936) Add capability to preprocess transactions before broadcasting from a higher level chain. -* (x/authz) [#13047](https://github.com/cosmos/cosmos-sdk/pull/13047) Add a GetAuthorization function to the keeper. -* (cli) [#13064](https://github.com/cosmos/cosmos-sdk/pull/13064) Add `debug prefixes` to list supported HRP prefixes via . -* (cli) [#12742](https://github.com/cosmos/cosmos-sdk/pull/12742) Add the `prune` CLI cmd to manually prune app store history versions based on the pruning options. -* (ledger) [#12935](https://github.com/cosmos/cosmos-sdk/pull/12935) Generalize Ledger integration to allow for different apps or keytypes that use SECP256k1. -* (x/bank) [#11981](https://github.com/cosmos/cosmos-sdk/pull/11981) Create the `SetSendEnabled` endpoint for managing the bank's SendEnabled settings. -* (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info. - ### Improvements * [#13323](https://github.com/cosmos/cosmos-sdk/pull/13323) Ensure `withdraw_rewards` rewards are emitted from all actions that result in rewards being withdrawn. -* [#13214](https://github.com/cosmos/cosmos-sdk/pull/13214) Add `withdraw-proposal` command to group module's CLI transaction commands. -* [#13070](https://github.com/cosmos/cosmos-sdk/pull/13070) Migrate from `gogo/protobuf` to `cosmos/gogoproto`. -* [#12981](https://github.com/cosmos/cosmos-sdk/pull/12981) Return proper error when parsing telemetry configuration. -* [#12995](https://github.com/cosmos/cosmos-sdk/pull/12995) Add `FormatTime` and `ParseTimeString` methods. -* [#12952](https://github.com/cosmos/cosmos-sdk/pull/12952) Replace keyring module to Cosmos fork. -* [#12352](https://github.com/cosmos/cosmos-sdk/pull/12352) Move the `RegisterSwaggerAPI` logic into a separate helper function in the server package. -* [#12876](https://github.com/cosmos/cosmos-sdk/pull/12876) Remove proposer-based rewards. -* [#12892](https://github.com/cosmos/cosmos-sdk/pull/12892) `make format` now runs only gofumpt and golangci-lint run ./... --fix, replacing `goimports` `gofmt` and `misspell` -* [#12846](https://github.com/cosmos/cosmos-sdk/pull/12846) Remove `RandomizedParams` from the `AppModuleSimulation` interface which is no longer needed. -* (events) [#12850](https://github.com/cosmos/cosmos-sdk/pull/12850) Add a new `fee_payer` attribute to the `tx` event that is emitted from the `DeductFeeDecorator` AnteHandler decorator. -* (ci) [#12854](https://github.com/cosmos/cosmos-sdk/pull/12854) Use ghcr.io to host the proto builder image. Update proto builder image to go 1.19 -* (x/bank) [#12706](https://github.com/cosmos/cosmos-sdk/pull/12706) Added the `chain-id` flag to the `AddTxFlagsToCmd` API. There is no longer a need to explicitly register this flag on commands whens `AddTxFlagsToCmd` is already called. -* [#12791](https://github.com/cosmos/cosmos-sdk/pull/12791) Bump the math library used in the sdk and replace old usages of sdk.\* -* (x/params) [#12615](https://github.com/cosmos/cosmos-sdk/pull/12615) Add `GetParamSetIfExists` function to params `Subspace` to prevent panics on breaking changes. -* [#12717](https://github.com/cosmos/cosmos-sdk/pull/12717) Use injected encoding params in simapp. -* (x/bank) [#12674](https://github.com/cosmos/cosmos-sdk/pull/12674) Add convenience function `CreatePrefixedAccountStoreKey()` to construct key to access account's balance for a given denom. -* [#12702](https://github.com/cosmos/cosmos-sdk/pull/12702) Linting and tidiness, fixed two minor security warnings. -* [#12634](https://github.com/cosmos/cosmos-sdk/pull/12634) Move `sdk.Dec` to math package. -* [#12596](https://github.com/cosmos/cosmos-sdk/pull/12596) Remove all imports of the non-existent gogo/protobuf v1.3.3 to ease downstream use and go workspaces. -* [#12187](https://github.com/cosmos/cosmos-sdk/pull/12187) Add batch operation for x/nft module. -* [#12693](https://github.com/cosmos/cosmos-sdk/pull/12693) Make sure the order of each node is consistent when emitting proto events. -* [#12455](https://github.com/cosmos/cosmos-sdk/pull/12455) Show attempts count in error for signing. -* [#12886](https://github.com/cosmos/cosmos-sdk/pull/12886) Amortize cost of processing cache KV store -* [#12953](https://github.com/cosmos/cosmos-sdk/pull/12953) Change the default priority mechanism to be based on gas price. -* [#13048](https://github.com/cosmos/cosmos-sdk/pull/13048) Add handling of AccountNumberStoreKeyPrefix to the x/auth simulation decoder. -* [#13101](https://github.com/cosmos/cosmos-sdk/pull/13101) Remove weights from `simapp/params` and `testutil/sims`. They are now in their respective modules. -* (simapp) [#13107](https://github.com/cosmos/cosmos-sdk/pull/13107) Call `SetIAVLCacheSize` with the configured value in simapp. -* [#12398](https://github.com/cosmos/cosmos-sdk/issues/12398) Refactor all `x` modules to unit-test via mocks and decouple `simapp`. -* [#13144](https://github.com/cosmos/cosmos-sdk/pull/13144) Add validator distribution info grpc gateway get endpoint. -* [#13168](https://github.com/cosmos/cosmos-sdk/pull/13168) Migrate tendermintdev/proto-builder to ghcr.io. New image `ghcr.io/cosmos/proto-builder:0.8` -* [#13178](https://github.com/cosmos/cosmos-sdk/pull/13178) Add `cosmos.msg.v1.service` protobuf annotation to allow tooling to distinguish between Msg and Query services via reflection. -* [#13233](https://github.com/cosmos/cosmos-sdk/pull/13233) Add `--append` to `add-genesis-account` sub-command to append new tokens after an account is already created. -* [#13236](https://github.com/cosmos/cosmos-sdk/pull/13236) Integrate Filter Logging -* [#13301](https://github.com/cosmos/cosmos-sdk/pull/13301) Keep the balance query endpoint compatible with legacy blocks -* [#13321](https://github.com/cosmos/cosmos-sdk/pull/13321) Add flag to disable fast node migration and usage. - -### State Machine Breaking -* (codec) [#13307](https://github.com/cosmos/cosmos-sdk/pull/13307) Register all modules' `Msg`s with group's ModuleCdc so that Amino sign bytes are correctly generated. -* (codec) [#13196](https://github.com/cosmos/cosmos-sdk/pull/13196) Register all modules' `Msg`s with gov's ModuleCdc so that Amino sign bytes are correctly generated. -* (x/distribution) [#12852](https://github.com/cosmos/cosmos-sdk/pull/12852) Deprecate `CommunityPoolSpendProposal`. Please execute a `MsgCommunityPoolSpend` message via the new v1 `x/gov` module instead. This message can be used to directly fund the `x/gov` module account. -* (x/bank) [#12610](https://github.com/cosmos/cosmos-sdk/pull/12610) `MsgMultiSend` now allows only a single input. -* (x/bank) [#12630](https://github.com/cosmos/cosmos-sdk/pull/12630) Migrate `x/bank` to self-managed parameters and deprecate its usage of `x/params`. -* (x/auth) [#12475](https://github.com/cosmos/cosmos-sdk/pull/12475) Migrate `x/auth` to self-managed parameters and deprecate its usage of `x/params`. -* (x/slashing) [#12399](https://github.com/cosmos/cosmos-sdk/pull/12399) Migrate `x/slashing` to self-managed parameters and deprecate its usage of `x/params`. -* (x/mint) [#12363](https://github.com/cosmos/cosmos-sdk/pull/12363) Migrate `x/mint` to self-managed parameters and deprecate it's usage of `x/params`. -* (x/distribution) [#12434](https://github.com/cosmos/cosmos-sdk/pull/12434) Migrate `x/distribution` to self-managed parameters and deprecate it's usage of `x/params`. -* (x/crisis) [#12445](https://github.com/cosmos/cosmos-sdk/pull/12445) Migrate `x/crisis` to self-managed parameters and deprecate it's usage of `x/params`. -* (x/gov) [#12631](https://github.com/cosmos/cosmos-sdk/pull/12631) Migrate `x/gov` to self-managed parameters and deprecate it's usage of `x/params`. -* (x/staking) [#12409](https://github.com/cosmos/cosmos-sdk/pull/12409) Migrate `x/staking` to self-managed parameters and deprecate it's usage of `x/params`. -* (x/bank) [#11859](https://github.com/cosmos/cosmos-sdk/pull/11859) Move the SendEnabled information out of the Params and into the state store directly. -* (x/gov) [#12771](https://github.com/cosmos/cosmos-sdk/pull/12771) Initial deposit requirement for proposals at submission time. -* (x/staking) [#12967](https://github.com/cosmos/cosmos-sdk/pull/12967) `unbond` now creates only one unbonding delegation entry when multiple unbondings exist at a single height (e.g. through multiple messages in a transaction). - ->>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) ### API Breaking Changes - (cli) [#13089](https://github.com/cosmos/cosmos-sdk/pull/13089) Fix rollback command don't actually delete multistore versions, added method `RollbackToVersion` to interface `CommitMultiStore` and added method `CommitMultiStore` to `Application` interface. diff --git a/tests/integration/distribution/keeper/delegation_test.go b/tests/integration/distribution/keeper/delegation_test.go deleted file mode 100644 index 0bb991158114..000000000000 --- a/tests/integration/distribution/keeper/delegation_test.go +++ /dev/null @@ -1,820 +0,0 @@ -package keeper_test - -import ( - "testing" - - "cosmossdk.io/math" - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/testutil" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func TestCalculateRewardsBasic(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) - - // end block to bond validator and start new block - staking.EndBlocker(ctx, stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - tstaking.Ctx = ctx - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // historical count should be 2 (once for validator init, once for delegation init) - require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // historical count should be 2 still - require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // calculate delegation rewards - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be zero - require.True(t, rewards.IsZero()) - - // allocate some rewards - initial := int64(10) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) - - // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestCalculateRewardsAfterSlash(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - valPower := int64(100) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be zero - require.True(t, rewards.IsZero()) - - // start out block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - - // retrieve validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // increase block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, rewards) - - // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, - distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestCalculateRewardsAfterManySlashes(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - // create validator with 50% commission - valPower := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be zero - require.True(t, rewards.IsZero()) - - // start out block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // increase block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // slash the validator by 50% again - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) - - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // increase block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, rewards) - - // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, - distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestCalculateRewardsMultiDelegator(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // allocate some rewards - initial := int64(20) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // second delegation - tstaking.Ctx = ctx - tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) - - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) - - // rewards for del1 should be 3/4 initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 3 / 4)}}, rewards) - - // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) - - // rewards for del2 should be 1/4 initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 1 / 4)}}, rewards) - - // commission should be equal to initial (50% twice) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestWithdrawDelegationRewardsBasic(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - balancePower := int64(1000) - balanceTokens := stakingKeeper.TokensFromConsensusPower(ctx, balancePower) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) - accountKeeper.SetModuleAccount(ctx, distrAcc) - - // create validator with 50% commission - power := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - valTokens := tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, power, true) - - // assert correct initial balance - expTokens := balanceTokens.Sub(valTokens) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, expTokens)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - - // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) - tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)} - - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // historical count should be 2 (initial + latest for delegation) - require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // withdraw rewards - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - require.Nil(t, err) - - // historical count should still be 2 (added one record, cleared one) - require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // assert correct balance - exp := balanceTokens.Sub(valTokens).Add(initial.QuoRaw(2)) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) - - // withdraw commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) - require.Nil(t, err) - - // assert correct balance - exp = balanceTokens.Sub(valTokens).Add(initial) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) -} - -func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - - // create validator with 50% commission - valPower := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be zero - require.True(t, rewards.IsZero()) - - // start out block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // allocate some rewards - initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 10)) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - - // slash the validator by 50% again - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) - - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // increase block height - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) - - // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards) - - // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - valPower := int64(100) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // allocate some rewards - initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 30)) - tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // slash the validator - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // second delegation - tstaking.DelegateWithPower(sdk.AccAddress(valAddrs[1]), valAddrs[0], 100) - - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // slash the validator again - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) - - // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) - - // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) - - // rewards for del2 should be initial / 3 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards) - - // commission should be equal to initial (twice 50% commission, unaffected by slashing) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) -} - -func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - initial := int64(20) - - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) - accountKeeper.SetModuleAccount(ctx, distrAcc) - - tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} - - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // allocate some rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // historical count should be 2 (validator init, delegation init) - require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // second delegation - tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) - - // historical count should be 3 (second delegation init) - require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // first delegator withdraws - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - require.NoError(t, err) - - // second delegator withdraws - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) - require.NoError(t, err) - - // historical count should be 3 (validator init + two delegations) - require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - - // validator withdraws commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) - require.NoError(t, err) - - // end period - endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) - - // rewards for del1 should be zero - require.True(t, rewards.IsZero()) - - // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) - - // rewards for del2 should be zero - require.True(t, rewards.IsZero()) - - // commission should be zero - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // first delegator withdraws again - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - require.NoError(t, err) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards for del1 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) - - // rewards for del1 should be zero - require.True(t, rewards.IsZero()) - - // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) - - // rewards for del2 should be 1/4 initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) - - // commission should be half initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // withdraw commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) - require.NoError(t, err) - - // end period - endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) - - // calculate delegation rewards for del1 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) - - // rewards for del1 should be 1/4 initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) - - // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) - - // rewards for del2 should be 1/2 initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) - - // commission should be zero - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) -} - -func Test100PercentCommissionReward(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - initial := int64(20) - - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) - accountKeeper.SetModuleAccount(ctx, distrAcc) - - tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} - - // create validator with 100% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) - stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // fetch validator - val := stakingKeeper.Validator(ctx, valAddrs[0]) - - // allocate some rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - require.NoError(t, err) - - zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())} - require.True(t, rewards.IsEqual(zeroRewards)) - - events := ctx.EventManager().Events() - lastEvent := events[len(events)-1] - - var hasValue bool - for _, attr := range lastEvent.Attributes { - if attr.Key == "amount" && attr.Value == "0stake" { - hasValue = true - } - } - require.True(t, hasValue) -} diff --git a/testutil/sims/app_helpers.go b/testutil/sims/app_helpers.go deleted file mode 100644 index 08aa4597e15a..000000000000 --- a/testutil/sims/app_helpers.go +++ /dev/null @@ -1,287 +0,0 @@ -package sims - -import ( - "encoding/json" - "fmt" - "time" - - abci "github.com/tendermint/tendermint/abci/types" - tmjson "github.com/tendermint/tendermint/libs/json" - "github.com/tendermint/tendermint/libs/log" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" - - "cosmossdk.io/depinject" - "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/runtime" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - "github.com/cosmos/cosmos-sdk/testutil/mock" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// SimAppChainID hardcoded chainID for simulation -const ( - DefaultGenTxGas = 10000000 - SimAppChainID = "simulation-app" -) - -// DefaultConsensusParams defines the default Tendermint consensus params used in -// SimApp testing. -var DefaultConsensusParams = &tmproto.ConsensusParams{ - Block: &tmproto.BlockParams{ - MaxBytes: 200000, - MaxGas: 2000000, - }, - Evidence: &tmproto.EvidenceParams{ - MaxAgeNumBlocks: 302400, - MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration - MaxBytes: 10000, - }, - Validator: &tmproto.ValidatorParams{ - PubKeyTypes: []string{ - tmtypes.ABCIPubKeyTypeEd25519, - }, - }, -} - -// CreateRandomValidatorSet creates a validator set with one random validator -func CreateRandomValidatorSet() (*tmtypes.ValidatorSet, error) { - privVal := mock.NewPV() - pubKey, err := privVal.GetPubKey() - if err != nil { - return nil, fmt.Errorf("failed to get pub key: %w", err) - } - - // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - - return tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}), nil -} - -type GenesisAccount struct { - authtypes.GenesisAccount - Coins sdk.Coins -} - -// StartupConfig defines the startup configuration new a test application. -// -// ValidatorSet defines a custom validator set to be validating the app. -// BaseAppOption defines the additional operations that must be run on baseapp before app start. -// AtGenesis defines if the app started should already have produced block or not. -type StartupConfig struct { - ValidatorSet func() (*tmtypes.ValidatorSet, error) - BaseAppOption runtime.BaseAppOption - AtGenesis bool - GenesisAccounts []GenesisAccount -} - -func DefaultStartUpConfig() StartupConfig { - priv := secp256k1.GenPrivKey() - ba := authtypes.NewBaseAccount(priv.PubKey().Address().Bytes(), priv.PubKey(), 0, 0) - ga := GenesisAccount{ba, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000)))} - return StartupConfig{ - ValidatorSet: CreateRandomValidatorSet, - AtGenesis: false, - GenesisAccounts: []GenesisAccount{ga}, - } -} - -// Setup initializes a new runtime.App and can inject values into extraOutputs. -// It uses SetupWithConfiguration under the hood. -func Setup(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithConfiguration(appConfig, DefaultStartUpConfig(), extraOutputs...) -} - -// SetupAtGenesis initializes a new runtime.App at genesis and can inject values into extraOutputs. -// It uses SetupWithConfiguration under the hood. -func SetupAtGenesis(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - cfg := DefaultStartUpConfig() - cfg.AtGenesis = true - return SetupWithConfiguration(appConfig, cfg, extraOutputs...) -} - -// SetupWithConfiguration initializes a new runtime.App. A Nop logger is set in runtime.App. -// appConfig usually load from a `app.yaml` with `appconfig.LoadYAML`, defines the application configuration. -// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). -func SetupWithConfiguration(appConfig depinject.Config, startupConfig StartupConfig, extraOutputs ...interface{}) (*runtime.App, error) { - // create the app with depinject - var ( - app *runtime.App - appBuilder *runtime.AppBuilder - codec codec.Codec - ) - - if err := depinject.Inject( - appConfig, - append(extraOutputs, &appBuilder, &codec)..., - ); err != nil { - return nil, fmt.Errorf("failed to inject dependencies: %w", err) - } - - if startupConfig.BaseAppOption != nil { - app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil, startupConfig.BaseAppOption) - } else { - app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil) - } - if err := app.Load(true); err != nil { - return nil, fmt.Errorf("failed to load app: %w", err) - } - - // create validator set - valSet, err := startupConfig.ValidatorSet() - if err != nil { - return nil, fmt.Errorf("failed to create validator set") - } - - var ( - balances []banktypes.Balance - genAccounts []authtypes.GenesisAccount - ) - for _, ga := range startupConfig.GenesisAccounts { - genAccounts = append(genAccounts, ga.GenesisAccount) - balances = append(balances, banktypes.Balance{Address: ga.GenesisAccount.GetAddress().String(), Coins: ga.Coins}) - } - - genesisState, err := GenesisStateWithValSet(codec, appBuilder.DefaultGenesis(), valSet, genAccounts, balances...) - if err != nil { - return nil, fmt.Errorf("failed to create genesis state: %w", err) - } - - // init chain must be called to stop deliverState from being nil - stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") - if err != nil { - return nil, fmt.Errorf("failed to marshal default genesis state: %w", err) - } - - // init chain will set the validator set and initialize the genesis accounts - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - - // commit genesis changes - if !startupConfig.AtGenesis { - app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ - Height: app.LastBlockHeight() + 1, - AppHash: app.LastCommitID().Hash, - ValidatorsHash: valSet.Hash(), - NextValidatorsHash: valSet.Hash(), - }}) - } - - return app, nil -} - -// GenesisStateWithValSet returns a new genesis state with the validator set -func GenesisStateWithValSet( - codec codec.Codec, - genesisState map[string]json.RawMessage, - valSet *tmtypes.ValidatorSet, - genAccs []authtypes.GenesisAccount, - balances ...banktypes.Balance, -) (map[string]json.RawMessage, error) { - // set genesis accounts - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis) - - validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) - delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) - - bondAmt := sdk.DefaultPowerReduction - - for _, val := range valSet.Validators { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) - if err != nil { - return nil, fmt.Errorf("failed to convert pubkey: %w", err) - } - - pkAny, err := codectypes.NewAnyWithValue(pk) - if err != nil { - return nil, fmt.Errorf("failed to create new any: %w", err) - } - - validator := stakingtypes.Validator{ - OperatorAddress: sdk.ValAddress(val.Address).String(), - ConsensusPubkey: pkAny, - Jailed: false, - Status: stakingtypes.Bonded, - Tokens: bondAmt, - DelegatorShares: math.LegacyOneDec(), - Description: stakingtypes.Description{}, - UnbondingHeight: int64(0), - UnbondingTime: time.Unix(0, 0).UTC(), - Commission: stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), - MinSelfDelegation: math.ZeroInt(), - } - validators = append(validators, validator) - delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), math.LegacyOneDec())) - - } - - // set validators and delegations - stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) - genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(stakingGenesis) - - totalSupply := sdk.NewCoins() - for _, b := range balances { - // add genesis acc tokens to total supply - totalSupply = totalSupply.Add(b.Coins...) - } - - for range delegations { - // add delegated tokens to total supply - totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) - } - - // add bonded amount to bonded pool module account - balances = append(balances, banktypes.Balance{ - Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, - }) - - // update total supply - bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) - genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis) - - return genesisState, nil -} - -// EmptyAppOptions is a stub implementing AppOptions -type EmptyAppOptions struct{} - -// Get implements AppOptions -func (ao EmptyAppOptions) Get(o string) interface{} { - return nil -} - -// AppOptionsMap is a stub implementing AppOptions which can get data from a map -type AppOptionsMap map[string]interface{} - -func (m AppOptionsMap) Get(key string) interface{} { - v, ok := m[key] - if !ok { - return interface{}(nil) - } - - return v -} - -func NewAppOptionsWithFlagHome(homePath string) servertypes.AppOptions { - return AppOptionsMap{ - flags.FlagHome: homePath, - } -} diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 4df0ff10d158..c70062c11d3f 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -3,12 +3,6 @@ package keeper import ( "fmt" -<<<<<<< HEAD - sdk "github.com/cosmos/cosmos-sdk/types" -======= - "cosmossdk.io/math" ->>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -202,7 +196,7 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali // Note, we do not call the NewCoins constructor as we do not want the zero // coin removed. - finalRewards = sdk.Coins{sdk.NewCoin(baseDenom, math.ZeroInt())} + finalRewards = sdk.Coins{sdk.NewCoin(baseDenom, sdk.ZeroInt())} } ctx.EventManager().EmitEvent( diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 7b2632604a38..8b4219e28028 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -6,13 +6,7 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" -<<<<<<< HEAD "github.com/cosmos/cosmos-sdk/simapp" -======= - "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/testutil" ->>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" @@ -629,97 +623,5 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, rewards) // commission should be zero -<<<<<<< HEAD require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) -======= - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission.IsZero()) -} - -func Test100PercentCommissionReward(t *testing.T) { - ctrl := gomock.NewController(t) - key := sdk.NewKVStoreKey(disttypes.StoreKey) - testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) - encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) - ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) - - bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) - stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) - accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) - - accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) - - distrKeeper := keeper.NewKeeper( - encCfg.Codec, - key, - accountKeeper, - bankKeeper, - stakingKeeper, - "fee_collector", - authtypes.NewModuleAddress("gov").String(), - ) - - // reset fee pool - distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) - distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - - // create validator with 50% commission - valAddr := sdk.ValAddress(valConsAddr0) - addr := sdk.AccAddress(valAddr) - val, err := distrtestutil.CreateValidator(valConsPk0, sdk.NewInt(100)) - require.NoError(t, err) - val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) - - // validator and delegation mocks - del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) - stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) - stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del).Times(3) - - // run the necessary hooks manually (given that we are not running an actual staking module) - err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) - require.NoError(t, err) - stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(2) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some rewards - initial := int64(20) - tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))} - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - // next block - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // allocate some more rewards - distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - - rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(addr), valAddr) - require.NoError(t, err) - - zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())} - require.True(t, rewards.IsEqual(zeroRewards)) - - events := ctx.EventManager().Events() - lastEvent := events[len(events)-1] - - var hasValue bool - for _, attr := range lastEvent.Attributes { - if attr.Key == "amount" && attr.Value == "0stake" { - hasValue = true - } - } - require.True(t, hasValue) ->>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 8b180cfba362..22e1a2894402 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -98,17 +98,6 @@ func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddres return nil, err } -<<<<<<< HEAD - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeWithdrawRewards, - sdk.NewAttribute(sdk.AttributeKeyAmount, rewards.String()), - sdk.NewAttribute(types.AttributeKeyValidator, valAddr.String()), - ), - ) - -======= ->>>>>>> c1c23a75d (fix: ensure withdraw_rewards events are always emitted on reward withdrawal (#13323)) // reinitialize the delegation k.initializeDelegation(ctx, valAddr, delAddr) return rewards, nil From a663638c515a7279c4e2add3080bd72adc95ba34 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 20 Sep 2022 14:32:53 +0200 Subject: [PATCH 3/4] move changelog to right place --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341fb08235b8..0aa7b1ac5082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * [#13323](https://github.com/cosmos/cosmos-sdk/pull/13323) Ensure `withdraw_rewards` rewards are emitted from all actions that result in rewards being withdrawn. +* [#13321](https://github.com/cosmos/cosmos-sdk/pull/13321) Add flag to disable fast node migration and usage. ### API Breaking Changes @@ -55,7 +56,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#12693](https://github.com/cosmos/cosmos-sdk/pull/12693) Make sure the order of each node is consistent when emitting proto events. * (simapp) [#13107](https://github.com/cosmos/cosmos-sdk/pull/13107) Call `SetIAVLCacheSize` with the configured value in simapp. * (cli) [#12742](https://github.com/cosmos/cosmos-sdk/pull/12742) Add the `prune` CLI cmd to manually prune app store history versions based on the pruning options. -* [#13321](https://github.com/cosmos/cosmos-sdk/pull/13321) Add flag to disable fast node migration and usage. ### Bug Fixes From 3b37ae309fb10de2b606fb8121d9ced2a4ce6d74 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 20 Sep 2022 14:45:34 +0200 Subject: [PATCH 4/4] fix typo --- server/config/config.go | 46 ++++++++++++++++++++--------------------- server/config/toml.go | 4 ++-- simapp/simd/cmd/root.go | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/server/config/config.go b/server/config/config.go index 7ca9b49d4b44..6925078158da 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -73,8 +73,8 @@ type BaseConfig struct { // IavlCacheSize set the size of the iavl tree cache. IAVLCacheSize uint64 `mapstructure:"iavl-cache-size"` - // IAVLDisableFastnNode enables or disables the fast sync node. - IAVLDisableFastnNode bool `mapstructure:"iavl-disable-fastnode"` + // IAVLDisableFastNode enables or disables the fast sync node. + IAVLDisableFastNode bool `mapstructure:"iavl-disable-fastnode"` } // APIConfig defines the API listener configuration. @@ -207,16 +207,16 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins { func DefaultConfig() *Config { return &Config{ BaseConfig: BaseConfig{ - MinGasPrices: defaultMinGasPrices, - InterBlockCache: true, - Pruning: storetypes.PruningOptionDefault, - PruningKeepRecent: "0", - PruningKeepEvery: "0", - PruningInterval: "0", - MinRetainBlocks: 0, - IndexEvents: make([]string, 0), - IAVLCacheSize: 781250, // 50 MB - IAVLDisableFastnNode: false, + MinGasPrices: defaultMinGasPrices, + InterBlockCache: true, + Pruning: storetypes.PruningOptionDefault, + PruningKeepRecent: "0", + PruningKeepEvery: "0", + PruningInterval: "0", + MinRetainBlocks: 0, + IndexEvents: make([]string, 0), + IAVLCacheSize: 781250, // 50 MB + IAVLDisableFastNode: false, }, Telemetry: telemetry.Config{ Enabled: false, @@ -273,17 +273,17 @@ func GetConfig(v *viper.Viper) (Config, error) { return Config{ BaseConfig: BaseConfig{ - MinGasPrices: v.GetString("minimum-gas-prices"), - InterBlockCache: v.GetBool("inter-block-cache"), - Pruning: v.GetString("pruning"), - PruningKeepRecent: v.GetString("pruning-keep-recent"), - PruningInterval: v.GetString("pruning-interval"), - HaltHeight: v.GetUint64("halt-height"), - HaltTime: v.GetUint64("halt-time"), - IndexEvents: v.GetStringSlice("index-events"), - MinRetainBlocks: v.GetUint64("min-retain-blocks"), - IAVLCacheSize: v.GetUint64("iavl-cache-size"), - IAVLDisableFastnNode: v.GetBool("iavl-disable-fastnode"), + MinGasPrices: v.GetString("minimum-gas-prices"), + InterBlockCache: v.GetBool("inter-block-cache"), + Pruning: v.GetString("pruning"), + PruningKeepRecent: v.GetString("pruning-keep-recent"), + PruningInterval: v.GetString("pruning-interval"), + HaltHeight: v.GetUint64("halt-height"), + HaltTime: v.GetUint64("halt-time"), + IndexEvents: v.GetStringSlice("index-events"), + MinRetainBlocks: v.GetUint64("min-retain-blocks"), + IAVLCacheSize: v.GetUint64("iavl-cache-size"), + IAVLDisableFastNode: v.GetBool("iavl-disable-fastnode"), }, Telemetry: telemetry.Config{ ServiceName: v.GetString("telemetry.service-name"), diff --git a/server/config/toml.go b/server/config/toml.go index 38234119cfba..91dee199a776 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -74,9 +74,9 @@ index-events = {{ .BaseConfig.IndexEvents }} # Default cache size is 50mb. iavl-cache-size = {{ .BaseConfig.IAVLCacheSize }} -# IavlDisableFastnNode enables or disables the fast node feature of IAVL. +# IAVLDisableFastNode enables or disables the fast node feature of IAVL. # Default is false. -iavl-disable-fastnode = {{ .BaseConfig.IAVLDisableFastnNode }} +iavl-disable-fastnode = {{ .BaseConfig.IAVLDisableFastNode }} ############################################################################### ### Telemetry Configuration ### diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 7a9c36d5244b..9f8660a27f54 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -118,7 +118,7 @@ func initAppConfig() (string, interface{}) { // // In simapp, we set the min gas prices to 0. srvCfg.MinGasPrices = "0stake" - // srvCfg.BaseConfig.IAVLDisableFastnNode = true // disable fastnode by default + // srvCfg.BaseConfig.IAVLDisableFastNode = true // disable fastnode by default customAppConfig := CustomAppConfig{ Config: *srvCfg,