refactor(x/distribution)!: Use KVStoreService, context.Context and return errors instead of panic (#15948)

This commit is contained in:
Facundo Medica 2023-04-27 10:26:51 -03:00 committed by GitHub
parent a6ea0948b2
commit 428e19f4f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1024 additions and 491 deletions

View File

@ -117,6 +117,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* (x/distribution) [#15948](https://github.com/cosmos/cosmos-sdk/issues/15948) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey` and methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context`. Keeper methods also now return an `error`.
* (x/bank) [#15891](https://github.com/cosmos/cosmos-sdk/issues/15891) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey` and methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context`. Also `FundAccount` and `FundModuleAccount` from the `testutil` package accept a `context.Context` instead of a `sdk.Context`, and it's position was moved to the first place.
* (x/bank) [#15818](https://github.com/cosmos/cosmos-sdk/issues/15818) `BaseViewKeeper`'s `Logger` method now doesn't require a context. `NewBaseKeeper`, `NewBaseSendKeeper` and `NewBaseViewKeeper` now also require a `log.Logger` to be passed in.
* (client) [#15597](https://github.com/cosmos/cosmos-sdk/pull/15597) `RegisterNodeService` now requires a config parameter.

View File

@ -73,6 +73,7 @@ The following modules `NewKeeper` function now take a `KVStoreService` instead o
* `x/auth`
* `x/bank`
* `x/consensus`
* `x/distribution`
* `x/feegrant`
* `x/nft`
@ -91,6 +92,11 @@ The following modules `NewKeeper` function now also take a `log.Logger`:
* `x/bank`
The following modules' `Keeper` methods now take in a `context.Context` instead of `sdk.Context`. Any module that has an interfaces for them (like "expected keepers") will need to update and re-generate mocks if needed:
* `x/bank`
* `x/distribution`
### depinject
@ -136,8 +142,6 @@ It is now recommended to validate message directly in the message server. When t
#### `x/auth`
Methods in the `AccountKeeper` now use `context.Context` instead of `sdk.Context`. Any module that has an interface for it will need to update and re-generate mocks if needed.
For ante handler construction via `ante.NewAnteHandler`, the field `ante.HandlerOptions.SignModeHandler` has been updated to `x/tx/signing/HandlerMap` from `x/auth/signing/SignModeHandler`. Callers typically fetch this value from `client.TxConfig.SignModeHandler()` (which is also changed) so this change should be transparent to most users.
#### `x/capability`

View File

@ -42,12 +42,12 @@ func getExtension(extID int32, m proto.Message) *gogoproto.ExtensionDesc {
for id, desc := range proto.RegisteredExtensions(m) { //nolint:staticcheck // keep for backward compatibility
if id == extID {
return &gogoproto.ExtensionDesc{
ExtendedType: desc.ExtendedType,
ExtensionType: desc.ExtensionType,
Field: desc.Field,
Name: desc.Name,
Tag: desc.Tag,
Filename: desc.Filename,
ExtendedType: desc.ExtendedType, //nolint:staticcheck // keep for backward compatibility
ExtensionType: desc.ExtensionType, //nolint:staticcheck // keep for backward compatibility
Field: desc.Field, //nolint:staticcheck // keep for backward compatibility
Name: desc.Name, //nolint:staticcheck // keep for backward compatibility
Tag: desc.Tag, //nolint:staticcheck // keep for backward compatibility
Filename: desc.Filename, //nolint:staticcheck // keep for backward compatibility
}
}
}

View File

@ -40,6 +40,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/runtime"
runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services"
@ -298,7 +299,7 @@ func NewSimApp(
)
app.MintKeeper = mintkeeper.NewKeeper(appCodec, keys[minttypes.StoreKey], app.StakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String())
app.DistrKeeper = distrkeeper.NewKeeper(appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String())
app.DistrKeeper = distrkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[distrtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String())
app.SlashingKeeper = slashingkeeper.NewKeeper(
appCodec, legacyAmino, keys[slashingtypes.StoreKey], app.StakingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(),
@ -624,7 +625,7 @@ func (app *SimApp) AutoCliOpts() autocli.AppOptions {
}
}
return autocli.AppOptions{Modules: modules}
return autocli.AppOptions{Modules: modules, AddressCodec: address.NewBech32Codec(sdk.Bech32MainPrefix)}
}
// DefaultGenesis returns a default genesis from the registered AppModuleBasic's.

View File

@ -107,10 +107,18 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []
// reinitialize all validators
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator())
feePool := app.DistrKeeper.GetFeePool(ctx)
scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator())
if err != nil {
panic(err)
}
feePool, err := app.DistrKeeper.GetFeePool(ctx)
if err != nil {
panic(err)
}
feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
app.DistrKeeper.SetFeePool(ctx, feePool)
if err := app.DistrKeeper.SetFeePool(ctx, feePool); err != nil {
panic(err)
}
if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()); err != nil {
panic(err)

View File

@ -100,8 +100,11 @@ func TestGRPCValidatorOutstandingRewards(t *testing.T) {
tstaking.CreateValidator(f.valAddr, valConsPk0, sdk.NewInt(initialStake), true)
// set outstanding rewards
f.distrKeeper.SetValidatorOutstandingRewards(f.sdkCtx, f.valAddr, types.ValidatorOutstandingRewards{Rewards: valCommission})
rewards := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, f.valAddr)
err := f.distrKeeper.SetValidatorOutstandingRewards(f.sdkCtx, f.valAddr, types.ValidatorOutstandingRewards{Rewards: valCommission})
assert.NilError(t, err)
rewards, err := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, f.valAddr)
assert.NilError(t, err)
testCases := []struct {
name string

View File

@ -10,7 +10,6 @@ import (
cmtabcitypes "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/stretchr/testify/require"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/codec"
@ -98,7 +97,7 @@ func initFixture(t testing.TB) *fixture {
stakingKeeper := stakingkeeper.NewKeeper(cdc, keys[stakingtypes.StoreKey], accountKeeper, bankKeeper, authority.String())
distrKeeper := distrkeeper.NewKeeper(
cdc, keys[distrtypes.StoreKey], accountKeeper, bankKeeper, stakingKeeper, distrtypes.ModuleName, authority.String(),
cdc, runtime.NewKVStoreService(keys[distrtypes.StoreKey]), accountKeeper, bankKeeper, stakingKeeper, distrtypes.ModuleName, authority.String(),
)
authModule := auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil)
@ -151,7 +150,8 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
CommunityPool: sdk.NewDecCoins(sdk.DecCoin{Denom: "stake", Amount: math.LegacyNewDec(10000)}),
})
f.distrKeeper.SetParams(f.sdkCtx, distrtypes.DefaultParams())
initFeePool := f.distrKeeper.GetFeePool(f.sdkCtx)
initFeePool, err := f.distrKeeper.GetFeePool(f.sdkCtx)
assert.NilError(t, err)
delAddr := sdk.AccAddress(PKS[1].Address())
valConsAddr := sdk.ConsAddress(valConsPk0.Address())
@ -195,7 +195,8 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3)
f.distrKeeper.SetValidatorCurrentRewards(f.sdkCtx, f.valAddr, currentRewards)
f.distrKeeper.SetValidatorOutstandingRewards(f.sdkCtx, f.valAddr, distrtypes.ValidatorOutstandingRewards{Rewards: valCommission})
initOutstandingRewards := f.distrKeeper.GetValidatorOutstandingRewardsCoins(f.sdkCtx, f.valAddr)
initOutstandingRewards, err := f.distrKeeper.GetValidatorOutstandingRewardsCoins(f.sdkCtx, f.valAddr)
assert.NilError(t, err)
testCases := []struct {
name string
@ -258,9 +259,10 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
},
}
height := f.app.LastBlockHeight()
require.Panics(t, func() {
f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
})
_, err = f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
assert.Error(t, err, "previous proposer not set")
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
@ -275,15 +277,6 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
panic(fmt.Errorf("expected block height to be %d, got %d", height, f.app.LastBlockHeight()))
}
prevProposerConsAddr := f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
assert.Assert(t, prevProposerConsAddr.Empty() == false)
assert.DeepEqual(t, prevProposerConsAddr, valConsAddr)
var previousTotalPower int64
for _, voteInfo := range f.sdkCtx.VoteInfos() {
previousTotalPower += voteInfo.Validator.Power
}
assert.Equal(t, previousTotalPower, int64(100))
if tc.expErr {
assert.ErrorContains(t, err, tc.expErrMsg)
} else {
@ -300,11 +293,21 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
assert.Assert(t, initBalance.IsAllLTE(curBalance))
// check rewards
curFeePool := f.distrKeeper.GetFeePool(f.sdkCtx)
curFeePool, _ := f.distrKeeper.GetFeePool(f.sdkCtx)
rewards := curFeePool.GetCommunityPool().Sub(initFeePool.CommunityPool)
curOutstandingRewards := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, f.valAddr)
curOutstandingRewards, _ := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, f.valAddr)
assert.DeepEqual(t, rewards, initOutstandingRewards.Sub(curOutstandingRewards.Rewards))
}
prevProposerConsAddr, err := f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
assert.NilError(t, err)
assert.Assert(t, prevProposerConsAddr.Empty() == false)
assert.DeepEqual(t, prevProposerConsAddr, valConsAddr)
var previousTotalPower int64
for _, voteInfo := range f.sdkCtx.VoteInfos() {
previousTotalPower += voteInfo.Validator.Power
}
assert.Equal(t, previousTotalPower, int64(100))
})
}
}
@ -328,7 +331,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "empty delegator address",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = true
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -342,7 +345,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "empty withdraw address",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = true
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -356,7 +359,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "both empty addresses",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = true
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -370,7 +373,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "withdraw address disabled",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = false
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -384,7 +387,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "valid msg with same delegator and withdraw address",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = true
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -397,7 +400,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{
name: "valid msg",
preRun: func() {
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
params.WithdrawAddrEnabled = true
assert.NilError(t, f.distrKeeper.SetParams(f.sdkCtx, params))
},
@ -421,7 +424,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
assert.ErrorContains(t, err, tc.expErrMsg)
// query the delegator withdraw address
addr := f.distrKeeper.GetDelegatorWithdrawAddr(f.sdkCtx, delAddr)
addr, _ := f.distrKeeper.GetDelegatorWithdrawAddr(f.sdkCtx, delAddr)
assert.DeepEqual(t, addr, delAddr)
} else {
assert.NilError(t, err)
@ -433,7 +436,7 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
assert.NilError(t, err)
// query the delegator withdraw address
addr := f.distrKeeper.GetDelegatorWithdrawAddr(f.sdkCtx, delAddr)
addr, _ := f.distrKeeper.GetDelegatorWithdrawAddr(f.sdkCtx, delAddr)
assert.DeepEqual(t, addr.String(), tc.msg.WithdrawAddress)
}
})
@ -529,11 +532,11 @@ func TestMsgWithdrawValidatorCommission(t *testing.T) {
), balance)
// check remainder
remainder := f.distrKeeper.GetValidatorAccumulatedCommission(f.sdkCtx, f.valAddr).Commission
remainder, _ := f.distrKeeper.GetValidatorAccumulatedCommission(f.sdkCtx, f.valAddr)
assert.DeepEqual(t, sdk.DecCoins{
sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(1).Quo(math.LegacyNewDec(4))),
sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(2))),
}, remainder)
}, remainder.Commission)
}
})
@ -546,7 +549,7 @@ func TestMsgFundCommunityPool(t *testing.T) {
// reset fee pool
f.distrKeeper.SetFeePool(f.sdkCtx, distrtypes.InitialFeePool())
initPool := f.distrKeeper.GetFeePool(f.sdkCtx)
initPool, _ := f.distrKeeper.GetFeePool(f.sdkCtx)
assert.Assert(t, initPool.CommunityPool.Empty())
initTokens := f.stakingKeeper.TokensFromConsensusPower(f.sdkCtx, int64(100))
@ -624,7 +627,8 @@ func TestMsgFundCommunityPool(t *testing.T) {
assert.NilError(t, err)
// query the community pool funds
assert.DeepEqual(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), f.distrKeeper.GetFeePool(f.sdkCtx).CommunityPool)
feePool, _ := f.distrKeeper.GetFeePool(f.sdkCtx)
assert.DeepEqual(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), feePool.CommunityPool)
assert.Assert(t, f.bankKeeper.GetAllBalances(f.sdkCtx, addr).Empty())
}
})
@ -750,7 +754,7 @@ func TestMsgUpdateParams(t *testing.T) {
assert.NilError(t, err)
// query the params and verify it has been updated
params := f.distrKeeper.GetParams(f.sdkCtx)
params, _ := f.distrKeeper.GetParams(f.sdkCtx)
assert.DeepEqual(t, distrtypes.DefaultParams(), params)
}
})
@ -765,7 +769,7 @@ func TestMsgCommunityPoolSpend(t *testing.T) {
f.distrKeeper.SetFeePool(f.sdkCtx, distrtypes.FeePool{
CommunityPool: sdk.NewDecCoins(sdk.DecCoin{Denom: "stake", Amount: math.LegacyNewDec(10000)}),
})
initialFeePool := f.distrKeeper.GetFeePool(f.sdkCtx)
initialFeePool, _ := f.distrKeeper.GetFeePool(f.sdkCtx)
initTokens := f.stakingKeeper.TokensFromConsensusPower(f.sdkCtx, int64(100))
f.bankKeeper.MintCoins(f.sdkCtx, distrtypes.ModuleName, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)))
@ -828,7 +832,7 @@ func TestMsgCommunityPoolSpend(t *testing.T) {
assert.NilError(t, err)
// query the community pool to verify it has been updated
communityPool := f.distrKeeper.GetFeePoolCommunityCoins(f.sdkCtx)
communityPool, _ := f.distrKeeper.GetFeePoolCommunityCoins(f.sdkCtx)
newPool, negative := initialFeePool.CommunityPool.SafeSub(sdk.NewDecCoinsFromCoins(tc.msg.Amount...))
assert.Assert(t, negative == false)
assert.DeepEqual(t, communityPool, newPool)
@ -928,7 +932,7 @@ func TestMsgDepositValidatorRewardsPool(t *testing.T) {
assert.NilError(t, err)
// check validator outstanding rewards
outstandingRewards := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, val)
outstandingRewards, _ := f.distrKeeper.GetValidatorOutstandingRewards(f.sdkCtx, val)
for _, c := range tc.msg.Amount {
x := outstandingRewards.Rewards.AmountOf(c.Denom)
assert.DeepEqual(t, x, sdk.NewDecFromInt(c.Amount))

View File

@ -286,7 +286,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/
```
```go
func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error
func (k Keeper) SetWithdrawAddr(ctx context.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error
if k.blockedAddrs[withdrawAddr.String()] {
fail with "`{withdrawAddr}` is not allowed to receive external funds"
}
@ -351,7 +351,7 @@ This message sends coins directly from the sender to the community pool.
The transaction fails if the amount cannot be transferred from the sender to the distribution module account.
```go
func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error {
func (k Keeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error {
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
return err
}
@ -375,7 +375,7 @@ Initializing a delegation increments the validator period and keeps track of the
```go
// initialize starting info for a new delegation
func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) {
func (k Keeper) initializeDelegation(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress) {
// period has already been incremented - we want to store the period ended by this delegation action
previousPeriod := k.GetValidatorCurrentRewards(ctx, val).Period - 1

View File

@ -1,21 +1,33 @@
package keeper
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// get outstanding rewards
func (k Keeper) GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.ValAddress) sdk.DecCoins {
return k.GetValidatorOutstandingRewards(ctx, val).Rewards
func (k Keeper) GetValidatorOutstandingRewardsCoins(ctx context.Context, val sdk.ValAddress) (sdk.DecCoins, error) {
rewards, err := k.GetValidatorOutstandingRewards(ctx, val)
if err != nil {
return nil, err
}
return rewards.Rewards, nil
}
// get the community coins
func (k Keeper) GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins {
return k.GetFeePool(ctx).CommunityPool
func (k Keeper) GetFeePoolCommunityCoins(ctx context.Context) (sdk.DecCoins, error) {
feePool, err := k.GetFeePool(ctx)
if err != nil {
return nil, err
}
return feePool.CommunityPool, nil
}
// GetDistributionAccount returns the distribution ModuleAccount
func (k Keeper) GetDistributionAccount(ctx sdk.Context) sdk.ModuleAccountI {
func (k Keeper) GetDistributionAccount(ctx context.Context) sdk.ModuleAccountI {
return k.authKeeper.GetModuleAccount(ctx, types.ModuleName)
}

View File

@ -1,6 +1,8 @@
package keeper
import (
"context"
"cosmossdk.io/math"
abci "github.com/cometbft/cometbft/abci/types"
@ -11,32 +13,40 @@ import (
// AllocateTokens performs reward and fee distribution to all validators based
// on the F1 fee distribution specification.
func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, bondedVotes []abci.VoteInfo) {
func (k Keeper) AllocateTokens(ctx context.Context, totalPreviousPower int64, bondedVotes []abci.VoteInfo) error {
// fetch and clear the collected fees for distribution, since this is
// called in BeginBlock, collected fees will be from the previous block
// (and distributed to the previous proposer)
sdkCtx := sdk.UnwrapSDKContext(ctx)
feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName)
feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress())
feesCollectedInt := k.bankKeeper.GetAllBalances(sdkCtx, feeCollector.GetAddress())
feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...)
// transfer collected fees to the distribution module account
err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt)
err := k.bankKeeper.SendCoinsFromModuleToModule(sdkCtx, k.feeCollectorName, types.ModuleName, feesCollectedInt)
if err != nil {
panic(err)
return err
}
// temporary workaround to keep CanWithdrawInvariant happy
// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
feePool := k.GetFeePool(ctx)
feePool, err := k.GetFeePool(ctx)
if err != nil {
return err
}
if totalPreviousPower == 0 {
feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...)
k.SetFeePool(ctx, feePool)
return
return k.SetFeePool(ctx, feePool)
}
// calculate fraction allocated to validators
remaining := feesCollected
communityTax := k.GetCommunityTax(ctx)
communityTax, err := k.GetCommunityTax(ctx)
if err != nil {
return err
}
voteMultiplier := math.LegacyOneDec().Sub(communityTax)
feeMultiplier := feesCollected.MulDecTruncate(voteMultiplier)
@ -46,7 +56,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, bonded
//
// Ref: https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376
for _, vote := range bondedVotes {
validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
validator := k.stakingKeeper.ValidatorByConsAddr(sdkCtx, vote.Validator.Address)
// TODO: Consider micro-slashing for missing votes.
//
@ -54,41 +64,60 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, bonded
powerFraction := math.LegacyNewDec(vote.Validator.Power).QuoTruncate(math.LegacyNewDec(totalPreviousPower))
reward := feeMultiplier.MulDecTruncate(powerFraction)
k.AllocateTokensToValidator(ctx, validator, reward)
err := k.AllocateTokensToValidator(ctx, validator, reward)
if err != nil {
return err
}
remaining = remaining.Sub(reward)
}
// allocate community funding
feePool.CommunityPool = feePool.CommunityPool.Add(remaining...)
k.SetFeePool(ctx, feePool)
return k.SetFeePool(ctx, feePool)
}
// AllocateTokensToValidator allocate tokens to a particular validator,
// splitting according to commission.
func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) {
func (k Keeper) AllocateTokensToValidator(ctx context.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) error {
// split tokens between validator and delegators according to commission
commission := tokens.MulDec(val.GetCommission())
shared := tokens.Sub(commission)
// update current commission
ctx.EventManager().EmitEvent(
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeCommission,
sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()),
),
)
currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
currentCommission, err := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
if err != nil {
return err
}
currentCommission.Commission = currentCommission.Commission.Add(commission...)
k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission)
err = k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission)
if err != nil {
return err
}
// update current rewards
currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
currentRewards, err := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
if err != nil {
return err
}
currentRewards.Rewards = currentRewards.Rewards.Add(shared...)
k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards)
err = k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards)
if err != nil {
return err
}
// update outstanding rewards
ctx.EventManager().EmitEvent(
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeRewards,
sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()),
@ -96,7 +125,11 @@ func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.Vali
),
)
outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
outstanding, err := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
if err != nil {
return err
}
outstanding.Rewards = outstanding.Rewards.Add(tokens...)
k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
return k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
}

View File

@ -12,6 +12,7 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
@ -26,6 +27,7 @@ import (
func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -38,7 +40,7 @@ func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -62,15 +64,21 @@ func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
expected := sdk.DecCoins{
{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(5)},
}
require.Equal(t, expected, distrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()).Commission)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
require.NoError(t, err)
require.Equal(t, expected, valCommission.Commission)
// check current rewards
require.Equal(t, expected, distrKeeper.GetValidatorCurrentRewards(ctx, val.GetOperator()).Rewards)
currentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, val.GetOperator())
require.NoError(t, err)
require.Equal(t, expected, currentRewards.Rewards)
}
func TestAllocateTokensToManyValidators(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -85,7 +93,7 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -121,13 +129,33 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
}
// assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsZero())
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero())
require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero())
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission.IsZero())
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero())
require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards.IsZero())
require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards.IsZero())
val0OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0OutstandingRewards.Rewards.IsZero())
val1OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1OutstandingRewards.Rewards.IsZero())
feePool, err := distrKeeper.GetFeePool(ctx)
require.NoError(t, err)
require.True(t, feePool.CommunityPool.IsZero())
val0Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0Commission.Commission.IsZero())
val1Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1Commission.Commission.IsZero())
val0CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0CurrentRewards.Rewards.IsZero())
val1CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1CurrentRewards.Rewards.IsZero())
// allocate tokens as if both had voted and second was proposer
fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100)))
@ -147,28 +175,44 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
distrKeeper.AllocateTokens(ctx, 200, votes)
// 98 outstanding rewards (100 less 2 to community pool)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards)
val0OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val0OutstandingRewards.Rewards)
val1OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val1OutstandingRewards.Rewards)
// 2 community pool coins
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, distrKeeper.GetFeePool(ctx).CommunityPool)
feePool, err = distrKeeper.GetFeePool(ctx)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, feePool.CommunityPool)
// 50% commission for first proposer, (0.5 * 98%) * 100 / 2 = 23.25
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission)
val0Commission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, val0Commission.Commission)
// zero commission for second proposer
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero())
val1Commission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1Commission.Commission.IsZero())
// just staking.proportional for first proposer less commission = (0.5 * 98%) * 100 / 2 = 24.50
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards)
val0CurrentRewards, err = distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, val0CurrentRewards.Rewards)
// proposer reward + staking.proportional for second proposer = (0.5 * (98%)) * 100 = 49
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards)
val1CurrentRewards, err = distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val1CurrentRewards.Rewards)
}
func TestAllocateTokensTruncation(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -183,7 +227,7 @@ func TestAllocateTokensTruncation(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -230,14 +274,33 @@ func TestAllocateTokensTruncation(t *testing.T) {
}
// assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsZero())
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero())
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero())
require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero())
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission.IsZero())
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero())
require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards.IsZero())
require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards.IsZero())
val0OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0OutstandingRewards.Rewards.IsZero())
val1OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1OutstandingRewards.Rewards.IsZero())
feePool, err := distrKeeper.GetFeePool(ctx)
require.NoError(t, err)
require.True(t, feePool.CommunityPool.IsZero())
val0Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0Commission.Commission.IsZero())
val1Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1Commission.Commission.IsZero())
val0CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0CurrentRewards.Rewards.IsZero())
val1CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1CurrentRewards.Rewards.IsZero())
// allocate tokens as if both had voted and second was proposer
fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(634195840)))
@ -260,7 +323,15 @@ func TestAllocateTokensTruncation(t *testing.T) {
}
distrKeeper.AllocateTokens(ctx, 31, votes)
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsValid())
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsValid())
require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr2).Rewards.IsValid())
val0OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
require.NoError(t, err)
require.True(t, val0OutstandingRewards.Rewards.IsValid())
val1OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
require.NoError(t, err)
require.True(t, val1OutstandingRewards.Rewards.IsValid())
val2OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr2)
require.NoError(t, err)
require.True(t, val2OutstandingRewards.Rewards.IsValid())
}

View File

@ -1,6 +1,7 @@
package keeper
import (
"context"
"fmt"
"cosmossdk.io/math"
@ -11,27 +12,32 @@ import (
)
// initialize starting info for a new delegation
func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) {
func (k Keeper) initializeDelegation(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress) error {
// period has already been incremented - we want to store the period ended by this delegation action
previousPeriod := k.GetValidatorCurrentRewards(ctx, val).Period - 1
valCurrentRewards, err := k.GetValidatorCurrentRewards(ctx, val)
if err != nil {
return err
}
previousPeriod := valCurrentRewards.Period - 1
// increment reference count for the period we're going to track
k.incrementReferenceCount(ctx, val, previousPeriod)
validator := k.stakingKeeper.Validator(ctx, val)
delegation := k.stakingKeeper.Delegation(ctx, del, val)
sdkCtx := sdk.UnwrapSDKContext(ctx)
validator := k.stakingKeeper.Validator(sdkCtx, val)
delegation := k.stakingKeeper.Delegation(sdkCtx, del, val)
// calculate delegation stake in tokens
// we don't store directly, so multiply delegation shares * (tokens per share)
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake := validator.TokensFromSharesTruncated(delegation.GetShares())
k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
return k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(sdkCtx.BlockHeight())))
}
// calculate the rewards accrued by a delegation between two periods
func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val stakingtypes.ValidatorI,
func (k Keeper) calculateDelegationRewardsBetween(ctx context.Context, val stakingtypes.ValidatorI,
startingPeriod, endingPeriod uint64, stake math.LegacyDec,
) (rewards sdk.DecCoins) {
) (sdk.DecCoins, error) {
// sanity check
if startingPeriod > endingPeriod {
panic("startingPeriod cannot be greater than endingPeriod")
@ -43,23 +49,35 @@ func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val stakingty
}
// return staking * (ending - starting)
starting := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
ending := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
starting, err := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
if err != nil {
return sdk.DecCoins{}, err
}
ending, err := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
if err != nil {
return sdk.DecCoins{}, err
}
difference := ending.CumulativeRewardRatio.Sub(starting.CumulativeRewardRatio)
if difference.IsAnyNegative() {
panic("negative rewards should not be possible")
}
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
rewards = difference.MulDecTruncate(stake)
return
rewards := difference.MulDecTruncate(stake)
return rewards, nil
}
// calculate the total rewards accrued by a delegation
func (k Keeper) CalculateDelegationRewards(ctx sdk.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI, endingPeriod uint64) (rewards sdk.DecCoins) {
func (k Keeper) CalculateDelegationRewards(ctx context.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI, endingPeriod uint64) (rewards sdk.DecCoins, err error) {
// fetch starting info for delegation
startingInfo := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
startingInfo, err := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
if err != nil {
return
}
if startingInfo.Height == uint64(ctx.BlockHeight()) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
if startingInfo.Height == uint64(sdkCtx.BlockHeight()) {
// started this height, no rewards yet
return
}
@ -77,13 +95,17 @@ func (k Keeper) CalculateDelegationRewards(ctx sdk.Context, val stakingtypes.Val
startingHeight := startingInfo.Height
// Slashes this block happened after reward allocation, but we have to account
// for them for the stake sanity check below.
endingHeight := uint64(ctx.BlockHeight())
endingHeight := uint64(sdkCtx.BlockHeight())
if endingHeight > startingHeight {
k.IterateValidatorSlashEventsBetween(ctx, del.GetValidatorAddr(), startingHeight, endingHeight,
func(height uint64, event types.ValidatorSlashEvent) (stop bool) {
endingPeriod := event.ValidatorPeriod
if endingPeriod > startingPeriod {
rewards = rewards.Add(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake)...)
delRewards, err := k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake)
if err != nil {
panic(err)
}
rewards = rewards.Add(delRewards...)
// Note: It is necessary to truncate so we don't allow withdrawing
// more rewards than owed.
@ -134,20 +156,41 @@ func (k Keeper) CalculateDelegationRewards(ctx sdk.Context, val stakingtypes.Val
}
// calculate rewards for final period
rewards = rewards.Add(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake)...)
return rewards
delRewards, err := k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake)
if err != nil {
return sdk.DecCoins{}, err
}
rewards = rewards.Add(delRewards...)
return rewards, nil
}
func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI) (sdk.Coins, error) {
func (k Keeper) withdrawDelegationRewards(ctx context.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI) (sdk.Coins, error) {
// check existence of delegator starting info
if !k.HasDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) {
hasInfo, err := k.HasDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
if err != nil {
return nil, err
}
if !hasInfo {
return nil, types.ErrEmptyDelegationDistInfo
}
// end current period and calculate rewards
endingPeriod := k.IncrementValidatorPeriod(ctx, val)
rewardsRaw := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
outstanding := k.GetValidatorOutstandingRewardsCoins(ctx, del.GetValidatorAddr())
endingPeriod, err := k.IncrementValidatorPeriod(ctx, val)
if err != nil {
return nil, err
}
rewardsRaw, err := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
if err != nil {
return nil, err
}
outstanding, err := k.GetValidatorOutstandingRewardsCoins(ctx, del.GetValidatorAddr())
if err != nil {
return nil, err
}
// defensive edge case may happen on the very final digits
// of the decCoins due to operation order of the distribution mechanism.
@ -168,8 +211,12 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali
// add coins to user account
if !finalRewards.IsZero() {
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr())
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, finalRewards)
withdrawAddr, err := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr())
if err != nil {
return nil, err
}
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, finalRewards)
if err != nil {
return nil, err
}
@ -177,18 +224,39 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali
// update the outstanding rewards and the community pool only if the
// transaction was successful
k.SetValidatorOutstandingRewards(ctx, del.GetValidatorAddr(), types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(rewards)})
feePool := k.GetFeePool(ctx)
err = k.SetValidatorOutstandingRewards(ctx, del.GetValidatorAddr(), types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(rewards)})
if err != nil {
return nil, err
}
feePool, err := k.GetFeePool(ctx)
if err != nil {
return nil, err
}
feePool.CommunityPool = feePool.CommunityPool.Add(remainder...)
k.SetFeePool(ctx, feePool)
err = k.SetFeePool(ctx, feePool)
if err != nil {
return nil, err
}
// decrement reference count of starting period
startingInfo := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
startingInfo, err := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
if err != nil {
return nil, err
}
startingPeriod := startingInfo.PreviousPeriod
k.decrementReferenceCount(ctx, del.GetValidatorAddr(), startingPeriod)
err = k.decrementReferenceCount(ctx, del.GetValidatorAddr(), startingPeriod)
if err != nil {
return nil, err
}
// remove delegator starting info
k.DeleteDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
err = k.DeleteDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
if err != nil {
return nil, err
}
if finalRewards.IsZero() {
baseDenom, _ := sdk.GetBaseDenom()
@ -201,7 +269,8 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.Vali
finalRewards = sdk.Coins{sdk.NewCoin(baseDenom, math.ZeroInt())}
}
ctx.EventManager().EmitEvent(
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeWithdrawRewards,
sdk.NewAttribute(sdk.AttributeKeyAmount, finalRewards.String()),

View File

@ -10,6 +10,7 @@ import (
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
@ -24,6 +25,7 @@ import (
func TestCalculateRewardsBasic(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -36,7 +38,7 @@ func TestCalculateRewardsBasic(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -70,13 +72,14 @@ func TestCalculateRewardsBasic(t *testing.T) {
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
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, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
@ -87,21 +90,25 @@ func TestCalculateRewardsBasic(t *testing.T) {
distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
}
func TestCalculateRewardsAfterSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -114,7 +121,7 @@ func TestCalculateRewardsAfterSlash(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -149,10 +156,11 @@ func TestCalculateRewardsAfterSlash(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
@ -181,22 +189,26 @@ func TestCalculateRewardsAfterSlash(t *testing.T) {
distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be half the tokens
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}}, rewards)
// commission should be the other half
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}},
distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission)
valCommission.Commission)
}
func TestCalculateRewardsAfterManySlashes(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -209,7 +221,7 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -243,10 +255,11 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
@ -296,22 +309,26 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) {
distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be half the tokens
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}, rewards)
// commission should be the other half
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}},
distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission)
valCommission.Commission)
}
func TestCalculateRewardsMultiDelegator(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -324,7 +341,7 @@ func TestCalculateRewardsMultiDelegator(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -381,27 +398,32 @@ func TestCalculateRewardsMultiDelegator(t *testing.T) {
distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del0, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del0, endingPeriod)
require.NoError(t, err)
// rewards for del0 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, del1, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, valCommission.Commission)
}
func TestWithdrawDelegationRewardsBasic(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -414,7 +436,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -474,6 +496,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -486,7 +509,7 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -519,10 +542,11 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
@ -566,21 +590,25 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
}
func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -593,7 +621,7 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -687,27 +715,32 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
}
func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -720,7 +753,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -810,22 +843,26 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, err)
// end period
endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards for del1 should be zero
require.True(t, rewards.IsZero())
// calculate delegation rewards for del2
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// rewards for del2 should be zero
require.True(t, rewards.IsZero())
// commission should be zero
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission.IsZero())
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.True(t, valCommission.Commission.IsZero())
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
@ -840,22 +877,26 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, err)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards for del1 should be zero
require.True(t, rewards.IsZero())
// calculate delegation rewards for del2
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission)
valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
@ -870,27 +911,32 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, err)
// end period
endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val)
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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, valAddr).Commission.IsZero())
valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.True(t, valCommission.Commission.IsZero())
}
func Test100PercentCommissionReward(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
@ -903,7 +949,7 @@ func Test100PercentCommissionReward(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,

View File

@ -1,14 +1,19 @@
package keeper
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// DistributeFromFeePool distributes funds from the distribution module account to
// a receiver address while updating the community pool
func (k Keeper) DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error {
feePool := k.GetFeePool(ctx)
func (k Keeper) DistributeFromFeePool(ctx context.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error {
feePool, err := k.GetFeePool(ctx)
if err != nil {
return err
}
// NOTE the community pool isn't a module account, however its coins
// are held in the distribution module account. Thus the community pool
@ -20,11 +25,10 @@ func (k Keeper) DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receive
feePool.CommunityPool = newPool
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiveAddr, amount)
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiveAddr, amount)
if err != nil {
return err
}
k.SetFeePool(ctx, feePool)
return nil
return k.SetFeePool(ctx, feePool)
}

View File

@ -109,8 +109,15 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
// ExportGenesis returns a GenesisState for a given context and keeper.
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
feePool := k.GetFeePool(ctx)
params := k.GetParams(ctx)
feePool, err := k.GetFeePool(ctx)
if err != nil {
panic(err)
}
params, err := k.GetParams(ctx)
if err != nil {
panic(err)
}
dwi := make([]types.DelegatorWithdrawInfo, 0)
k.IterateDelegatorWithdrawAddrs(ctx, func(del, addr sdk.AccAddress) (stop bool) {
@ -121,7 +128,11 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
return false
})
pp := k.GetPreviousProposerConsAddr(ctx)
pp, err := k.GetPreviousProposerConsAddr(ctx)
if err != nil {
panic(err)
}
outstanding := make([]types.ValidatorOutstandingRewardsRecord, 0)
k.IterateValidatorOutstandingRewards(ctx,

View File

@ -9,6 +9,7 @@ import (
"cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
@ -27,8 +28,10 @@ func NewQuerier(keeper Keeper) Querier {
// Params queries params of distribution module
func (k Querier) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
params := k.GetParams(ctx)
params, err := k.GetParams(c)
if err != nil {
return nil, err
}
return &types.QueryParamsResponse{Params: params}, nil
}
@ -63,11 +66,21 @@ func (k Querier) ValidatorDistributionInfo(c context.Context, req *types.QueryVa
return nil, types.ErrNoDelegationExists
}
endingPeriod := k.IncrementValidatorPeriod(ctx, val)
rewards := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
endingPeriod, err := k.IncrementValidatorPeriod(ctx, val)
if err != nil {
return nil, err
}
rewards, err := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
if err != nil {
return nil, err
}
// validator's commission
validatorCommission := k.GetValidatorAccumulatedCommission(ctx, valAdr)
validatorCommission, err := k.GetValidatorAccumulatedCommission(ctx, valAdr)
if err != nil {
return nil, err
}
return &types.QueryValidatorDistributionInfoResponse{
Commission: validatorCommission.Commission,
@ -97,7 +110,11 @@ func (k Querier) ValidatorOutstandingRewards(c context.Context, req *types.Query
if validator == nil {
return nil, errors.Wrapf(types.ErrNoValidatorExists, valAdr.String())
}
rewards := k.GetValidatorOutstandingRewards(ctx, valAdr)
rewards, err := k.GetValidatorOutstandingRewards(ctx, valAdr)
if err != nil {
return nil, err
}
return &types.QueryValidatorOutstandingRewardsResponse{Rewards: rewards}, nil
}
@ -123,7 +140,10 @@ func (k Querier) ValidatorCommission(c context.Context, req *types.QueryValidato
if validator == nil {
return nil, errors.Wrapf(types.ErrNoValidatorExists, valAdr.String())
}
commission := k.GetValidatorAccumulatedCommission(ctx, valAdr)
commission, err := k.GetValidatorAccumulatedCommission(ctx, valAdr)
if err != nil {
return nil, err
}
return &types.QueryValidatorCommissionResponse{Commission: commission}, nil
}
@ -142,12 +162,12 @@ func (k Querier) ValidatorSlashes(c context.Context, req *types.QueryValidatorSl
return nil, status.Errorf(codes.InvalidArgument, "starting height greater than ending height (%d > %d)", req.StartingHeight, req.EndingHeight)
}
ctx := sdk.UnwrapSDKContext(c)
store := ctx.KVStore(k.storeKey)
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddress)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid validator address")
}
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(c))
slashesStore := prefix.NewStore(store, types.GetValidatorSlashEventPrefix(valAddr))
events, pageRes, err := query.GenericFilteredPaginate(k.cdc, slashesStore, req.Pagination, func(key []byte, result *types.ValidatorSlashEvent) (*types.ValidatorSlashEvent, error) {
@ -206,8 +226,15 @@ func (k Querier) DelegationRewards(c context.Context, req *types.QueryDelegation
return nil, types.ErrNoDelegationExists
}
endingPeriod := k.IncrementValidatorPeriod(ctx, val)
rewards := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
endingPeriod, err := k.IncrementValidatorPeriod(ctx, val)
if err != nil {
return nil, err
}
rewards, err := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
if err != nil {
return nil, err
}
return &types.QueryDelegationRewardsResponse{Rewards: rewards}, nil
}
@ -237,8 +264,15 @@ func (k Querier) DelegationTotalRewards(c context.Context, req *types.QueryDeleg
func(_ int64, del stakingtypes.DelegationI) (stop bool) {
valAddr := del.GetValidatorAddr()
val := k.stakingKeeper.Validator(ctx, valAddr)
endingPeriod := k.IncrementValidatorPeriod(ctx, val)
delReward := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
endingPeriod, err := k.IncrementValidatorPeriod(ctx, val)
if err != nil {
panic(err)
}
delReward, err := k.CalculateDelegationRewards(ctx, val, del, endingPeriod)
if err != nil {
panic(err)
}
delRewards = append(delRewards, types.NewDelegationDelegatorReward(valAddr, delReward))
total = total.Add(delReward...)
@ -291,16 +325,20 @@ func (k Querier) DelegatorWithdrawAddress(c context.Context, req *types.QueryDel
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delAdr)
withdrawAddr, err := k.GetDelegatorWithdrawAddr(c, delAdr)
if err != nil {
return nil, err
}
return &types.QueryDelegatorWithdrawAddressResponse{WithdrawAddress: withdrawAddr.String()}, nil
}
// CommunityPool queries the community pool coins
func (k Querier) CommunityPool(c context.Context, req *types.QueryCommunityPoolRequest) (*types.QueryCommunityPoolResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
pool := k.GetFeePoolCommunityCoins(ctx)
pool, err := k.GetFeePoolCommunityCoins(c)
if err != nil {
return nil, err
}
return &types.QueryCommunityPoolResponse{Pool: pool}, nil
}

View File

@ -23,17 +23,25 @@ func (k Keeper) Hooks() Hooks {
// initialize validator distribution record
func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error {
val := h.k.stakingKeeper.Validator(ctx, valAddr)
h.k.initializeValidator(ctx, val)
return nil
return h.k.initializeValidator(ctx, val)
}
// AfterValidatorRemoved performs clean up after a validator is removed
func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) error {
// fetch outstanding
outstanding := h.k.GetValidatorOutstandingRewardsCoins(ctx, valAddr)
outstanding, err := h.k.GetValidatorOutstandingRewardsCoins(ctx, valAddr)
if err != nil {
return err
}
// force-withdraw commission
commission := h.k.GetValidatorAccumulatedCommission(ctx, valAddr).Commission
valCommission, err := h.k.GetValidatorAccumulatedCommission(ctx, valAddr)
if err != nil {
return err
}
commission := valCommission.Commission
if !commission.IsZero() {
// subtract from outstanding
outstanding = outstanding.Sub(commission)
@ -42,14 +50,24 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr
coins, remainder := commission.TruncateDecimal()
// remainder to community pool
feePool := h.k.GetFeePool(ctx)
feePool, err := h.k.GetFeePool(ctx)
if err != nil {
return err
}
feePool.CommunityPool = feePool.CommunityPool.Add(remainder...)
h.k.SetFeePool(ctx, feePool)
err = h.k.SetFeePool(ctx, feePool)
if err != nil {
return err
}
// add to validator account
if !coins.IsZero() {
accAddr := sdk.AccAddress(valAddr)
withdrawAddr := h.k.GetDelegatorWithdrawAddr(ctx, accAddr)
withdrawAddr, err := h.k.GetDelegatorWithdrawAddr(ctx, accAddr)
if err != nil {
return err
}
if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins); err != nil {
return err
@ -60,15 +78,28 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr
// Add outstanding to community pool
// The validator is removed only after it has no more delegations.
// This operation sends only the remaining dust to the community pool.
feePool := h.k.GetFeePool(ctx)
feePool, err := h.k.GetFeePool(ctx)
if err != nil {
return err
}
feePool.CommunityPool = feePool.CommunityPool.Add(outstanding...)
h.k.SetFeePool(ctx, feePool)
err = h.k.SetFeePool(ctx, feePool)
if err != nil {
return err
}
// delete outstanding
h.k.DeleteValidatorOutstandingRewards(ctx, valAddr)
err = h.k.DeleteValidatorOutstandingRewards(ctx, valAddr)
if err != nil {
return err
}
// remove commission record
h.k.DeleteValidatorAccumulatedCommission(ctx, valAddr)
err = h.k.DeleteValidatorAccumulatedCommission(ctx, valAddr)
if err != nil {
return err
}
// clear slashes
h.k.DeleteValidatorSlashEvents(ctx, valAddr)
@ -77,7 +108,10 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr
h.k.DeleteValidatorHistoricalRewards(ctx, valAddr)
// clear current rewards
h.k.DeleteValidatorCurrentRewards(ctx, valAddr)
err = h.k.DeleteValidatorCurrentRewards(ctx, valAddr)
if err != nil {
return err
}
return nil
}
@ -85,8 +119,8 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr
// increment period
func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
val := h.k.stakingKeeper.Validator(ctx, valAddr)
_ = h.k.IncrementValidatorPeriod(ctx, val)
return nil
_, err := h.k.IncrementValidatorPeriod(ctx, val)
return err
}
// withdraw delegation rewards (which also increments period)
@ -103,8 +137,7 @@ func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAd
// create new delegation period record
func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
h.k.initializeDelegation(ctx, valAddr, delAddr)
return nil
return h.k.initializeDelegation(ctx, valAddr, delAddr)
}
// record the slash event

View File

@ -88,7 +88,12 @@ func CanWithdrawInvariant(k Keeper) sdk.Invariant {
}
}
remaining = k.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator())
var err error
remaining, err = k.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator())
if err != nil {
panic(err)
}
if len(remaining) > 0 && remaining[0].Amount.IsNegative() {
return true
}
@ -141,7 +146,11 @@ func ModuleAccountInvariant(k Keeper) sdk.Invariant {
return false
})
communityPool := k.GetFeePoolCommunityCoins(ctx)
communityPool, err := k.GetFeePoolCommunityCoins(ctx)
if err != nil {
panic(err)
}
expectedInt, _ := expectedCoins.Add(communityPool...).TruncateDecimal()
macc := k.GetDistributionAccount(ctx)

View File

@ -1,11 +1,12 @@
package keeper
import (
"context"
"fmt"
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -15,7 +16,7 @@ import (
// Keeper of the distribution store
type Keeper struct {
storeKey storetypes.StoreKey
storeService store.KVStoreService
cdc codec.BinaryCodec
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
@ -29,7 +30,7 @@ type Keeper struct {
// NewKeeper creates a new distribution Keeper instance
func NewKeeper(
cdc codec.BinaryCodec, key storetypes.StoreKey,
cdc codec.BinaryCodec, storeService store.KVStoreService,
ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper,
feeCollectorName, authority string,
) Keeper {
@ -39,7 +40,7 @@ func NewKeeper(
}
return Keeper{
storeKey: key,
storeService: storeService,
cdc: cdc,
authKeeper: ak,
bankKeeper: bk,
@ -55,21 +56,28 @@ func (k Keeper) GetAuthority() string {
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
func (k Keeper) Logger(ctx context.Context) log.Logger {
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.Logger().With(log.ModuleKey, "x/"+types.ModuleName)
}
// SetWithdrawAddr sets a new address that will receive the rewards upon withdrawal
func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr, withdrawAddr sdk.AccAddress) error {
func (k Keeper) SetWithdrawAddr(ctx context.Context, delegatorAddr, withdrawAddr sdk.AccAddress) error {
if k.bankKeeper.BlockedAddr(withdrawAddr) {
return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", withdrawAddr)
}
if !k.GetWithdrawAddrEnabled(ctx) {
withdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx)
if err != nil {
return err
}
if !withdrawAddrEnabled {
return types.ErrSetWithdrawAddrDisabled
}
ctx.EventManager().EmitEvent(
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSetWithdrawAddress,
sdk.NewAttribute(types.AttributeKeyWithdrawAddress, withdrawAddr.String()),
@ -81,32 +89,40 @@ func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr, withdrawAddr sdk
}
// withdraw rewards from a delegation
func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
val := k.stakingKeeper.Validator(ctx, valAddr)
func (k Keeper) WithdrawDelegationRewards(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
val := k.stakingKeeper.Validator(sdkCtx, valAddr)
if val == nil {
return nil, types.ErrNoValidatorDistInfo
}
del := k.stakingKeeper.Delegation(ctx, delAddr, valAddr)
del := k.stakingKeeper.Delegation(sdkCtx, delAddr, valAddr)
if del == nil {
return nil, types.ErrEmptyDelegationDistInfo
}
// withdraw rewards
rewards, err := k.withdrawDelegationRewards(ctx, val, del)
rewards, err := k.withdrawDelegationRewards(sdkCtx, val, del)
if err != nil {
return nil, err
}
// reinitialize the delegation
k.initializeDelegation(ctx, valAddr, delAddr)
err = k.initializeDelegation(sdkCtx, valAddr, delAddr)
if err != nil {
return nil, err
}
return rewards, nil
}
// withdraw validator commission
func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddress) (sdk.Coins, error) {
func (k Keeper) WithdrawValidatorCommission(ctx context.Context, valAddr sdk.ValAddress) (sdk.Coins, error) {
// fetch validator accumulated commission
accumCommission := k.GetValidatorAccumulatedCommission(ctx, valAddr)
accumCommission, err := k.GetValidatorAccumulatedCommission(ctx, valAddr)
if err != nil {
return nil, err
}
if accumCommission.Commission.IsZero() {
return nil, types.ErrNoValidatorCommission
}
@ -115,19 +131,31 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr
k.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: remainder}) // leave remainder to withdraw later
// update outstanding
outstanding := k.GetValidatorOutstandingRewards(ctx, valAddr).Rewards
k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(sdk.NewDecCoinsFromCoins(commission...))})
outstanding, err := k.GetValidatorOutstandingRewards(ctx, valAddr)
if err != nil {
return nil, err
}
err = k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Rewards.Sub(sdk.NewDecCoinsFromCoins(commission...))})
if err != nil {
return nil, err
}
if !commission.IsZero() {
accAddr := sdk.AccAddress(valAddr)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr)
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
withdrawAddr, err := k.GetDelegatorWithdrawAddr(ctx, accAddr)
if err != nil {
return nil, err
}
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
if err != nil {
return nil, err
}
}
ctx.EventManager().EmitEvent(
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeWithdrawCommission,
sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
@ -138,7 +166,7 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr
}
// GetTotalRewards returns the total amount of fee distribution rewards held in the store
func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) {
func (k Keeper) GetTotalRewards(ctx context.Context) (totalRewards sdk.DecCoins) {
k.IterateValidatorOutstandingRewards(ctx,
func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
totalRewards = totalRewards.Add(rewards.Rewards...)
@ -153,14 +181,16 @@ func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) {
// The amount is first added to the distribution module account and then directly
// added to the pool. An error is returned if the amount cannot be sent to the
// module account.
func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error {
func (k Keeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error {
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
return err
}
feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
k.SetFeePool(ctx, feePool)
feePool, err := k.GetFeePool(ctx)
if err != nil {
return err
}
return nil
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
return k.SetFeePool(ctx, feePool)
}

View File

@ -11,6 +11,7 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -25,6 +26,7 @@ import (
func TestSetWithdrawAddr(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(types.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -44,7 +46,7 @@ func TestSetWithdrawAddr(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -71,6 +73,7 @@ func TestSetWithdrawAddr(t *testing.T) {
func TestWithdrawValidatorCommission(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(types.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -91,7 +94,7 @@ func TestWithdrawValidatorCommission(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -114,18 +117,19 @@ func TestWithdrawValidatorCommission(t *testing.T) {
require.NoError(t, err)
// check remainder
remainder := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission
remainderValCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
remainder := remainderValCommission.Commission
require.Equal(t, sdk.DecCoins{
sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(1).Quo(math.LegacyNewDec(4))),
sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(2))),
}, remainder)
require.True(t, true)
}
func TestGetTotalRewards(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(types.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -142,7 +146,7 @@ func TestGetTotalRewards(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -167,6 +171,7 @@ func TestGetTotalRewards(t *testing.T) {
func TestFundCommunityPool(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(types.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
@ -180,7 +185,7 @@ func TestFundCommunityPool(t *testing.T) {
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
key,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
@ -191,13 +196,16 @@ func TestFundCommunityPool(t *testing.T) {
// reset fee pool
distrKeeper.SetFeePool(ctx, types.InitialFeePool())
initPool := distrKeeper.GetFeePool(ctx)
initPool, err := distrKeeper.GetFeePool(ctx)
require.NoError(t, err)
require.Empty(t, initPool.CommunityPool)
amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), addrs[0], "distribution", amount).Return(nil)
err := distrKeeper.FundCommunityPool(ctx, amount, addrs[0])
require.Nil(t, err)
err = distrKeeper.FundCommunityPool(ctx, amount, addrs[0])
require.NoError(t, err)
require.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), distrKeeper.GetFeePool(ctx).CommunityPool)
feePool, err := distrKeeper.GetFeePool(ctx)
require.NoError(t, err)
require.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), feePool.CommunityPool)
}

View File

@ -20,7 +20,7 @@ func NewMigrator(keeper Keeper, legacySubspace exported.Subspace) Migrator {
// Migrate1to2 migrates from version 1 to 2.
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v2.MigrateStore(ctx, m.keeper.storeKey)
return v2.MigrateStore(ctx, m.keeper.storeService)
}
// Migrate2to3 migrates the x/distribution module state from the consensus
@ -28,5 +28,5 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error {
// and managed by the x/params module and stores them directly into the x/distribution
// module state.
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
return v3.MigrateStore(ctx, m.keeper.storeKey, m.legacySubspace, m.keeper.cdc)
return v3.MigrateStore(ctx, m.keeper.storeService, m.legacySubspace, m.keeper.cdc)
}

View File

@ -25,7 +25,7 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{Keeper: keeper}
}
func (k msgServer) SetWithdrawAddress(goCtx context.Context, msg *types.MsgSetWithdrawAddress) (*types.MsgSetWithdrawAddressResponse, error) {
func (k msgServer) SetWithdrawAddress(ctx context.Context, msg *types.MsgSetWithdrawAddress) (*types.MsgSetWithdrawAddressResponse, error) {
delegatorAddress, err := k.authKeeper.StringToBytes(msg.DelegatorAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid delegator address: %s", err)
@ -36,7 +36,6 @@ func (k msgServer) SetWithdrawAddress(goCtx context.Context, msg *types.MsgSetWi
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid withdraw address: %s", err)
}
ctx := sdk.UnwrapSDKContext(goCtx)
err = k.SetWithdrawAddr(ctx, delegatorAddress, withdrawAddress)
if err != nil {
return nil, err
@ -45,7 +44,7 @@ func (k msgServer) SetWithdrawAddress(goCtx context.Context, msg *types.MsgSetWi
return &types.MsgSetWithdrawAddressResponse{}, nil
}
func (k msgServer) WithdrawDelegatorReward(goCtx context.Context, msg *types.MsgWithdrawDelegatorReward) (*types.MsgWithdrawDelegatorRewardResponse, error) {
func (k msgServer) WithdrawDelegatorReward(ctx context.Context, msg *types.MsgWithdrawDelegatorReward) (*types.MsgWithdrawDelegatorRewardResponse, error) {
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid validator address: %s", err)
@ -56,7 +55,6 @@ func (k msgServer) WithdrawDelegatorReward(goCtx context.Context, msg *types.Msg
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid delegator address: %s", err)
}
ctx := sdk.UnwrapSDKContext(goCtx)
amount, err := k.WithdrawDelegationRewards(ctx, delegatorAddress, valAddr)
if err != nil {
return nil, err
@ -77,13 +75,12 @@ func (k msgServer) WithdrawDelegatorReward(goCtx context.Context, msg *types.Msg
return &types.MsgWithdrawDelegatorRewardResponse{Amount: amount}, nil
}
func (k msgServer) WithdrawValidatorCommission(goCtx context.Context, msg *types.MsgWithdrawValidatorCommission) (*types.MsgWithdrawValidatorCommissionResponse, error) {
func (k msgServer) WithdrawValidatorCommission(ctx context.Context, msg *types.MsgWithdrawValidatorCommission) (*types.MsgWithdrawValidatorCommissionResponse, error) {
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid validator address: %s", err)
}
ctx := sdk.UnwrapSDKContext(goCtx)
amount, err := k.Keeper.WithdrawValidatorCommission(ctx, valAddr)
if err != nil {
return nil, err
@ -104,7 +101,7 @@ func (k msgServer) WithdrawValidatorCommission(goCtx context.Context, msg *types
return &types.MsgWithdrawValidatorCommissionResponse{Amount: amount}, nil
}
func (k msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCommunityPool) (*types.MsgFundCommunityPoolResponse, error) {
func (k msgServer) FundCommunityPool(ctx context.Context, msg *types.MsgFundCommunityPool) (*types.MsgFundCommunityPoolResponse, error) {
depositor, err := k.authKeeper.StringToBytes(msg.Depositor)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid depositor address: %s", err)
@ -114,7 +111,6 @@ func (k msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCo
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
if err := k.Keeper.FundCommunityPool(ctx, msg.Amount, depositor); err != nil {
return nil, err
}
@ -122,7 +118,7 @@ func (k msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCo
return &types.MsgFundCommunityPoolResponse{}, nil
}
func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
func (k msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
if err := k.validateAuthority(msg.Authority); err != nil {
return nil, err
}
@ -136,7 +132,6 @@ func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParam
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
if err := k.SetParams(ctx, msg.Params); err != nil {
return nil, err
}
@ -144,7 +139,7 @@ func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParam
return &types.MsgUpdateParamsResponse{}, nil
}
func (k msgServer) CommunityPoolSpend(goCtx context.Context, msg *types.MsgCommunityPoolSpend) (*types.MsgCommunityPoolSpendResponse, error) {
func (k msgServer) CommunityPoolSpend(ctx context.Context, msg *types.MsgCommunityPoolSpend) (*types.MsgCommunityPoolSpendResponse, error) {
if err := k.validateAuthority(msg.Authority); err != nil {
return nil, err
}
@ -162,7 +157,6 @@ func (k msgServer) CommunityPoolSpend(goCtx context.Context, msg *types.MsgCommu
return nil, errors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", msg.Recipient)
}
ctx := sdk.UnwrapSDKContext(goCtx)
if err := k.DistributeFromFeePool(ctx, msg.Amount, recipient); err != nil {
return nil, err
}
@ -173,13 +167,12 @@ func (k msgServer) CommunityPoolSpend(goCtx context.Context, msg *types.MsgCommu
return &types.MsgCommunityPoolSpendResponse{}, nil
}
func (k msgServer) DepositValidatorRewardsPool(goCtx context.Context, msg *types.MsgDepositValidatorRewardsPool) (*types.MsgDepositValidatorRewardsPoolResponse, error) {
func (k msgServer) DepositValidatorRewardsPool(ctx context.Context, msg *types.MsgDepositValidatorRewardsPool) (*types.MsgDepositValidatorRewardsPoolResponse, error) {
depositor, err := k.authKeeper.StringToBytes(msg.Depositor)
if err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
// deposit coins from depositor's account to the distribution module
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, depositor, types.ModuleName, msg.Amount); err != nil {
return nil, err
@ -190,7 +183,8 @@ func (k msgServer) DepositValidatorRewardsPool(goCtx context.Context, msg *types
return nil, err
}
validator := k.stakingKeeper.Validator(ctx, valAddr)
sdkCtx := sdk.UnwrapSDKContext(ctx)
validator := k.stakingKeeper.Validator(sdkCtx, valAddr)
if validator == nil {
return nil, errors.Wrapf(types.ErrNoValidatorExists, valAddr.String())
}

View File

@ -1,44 +1,53 @@
package keeper
import (
"context"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// GetParams returns the total set of distribution parameters.
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.ParamsKey)
if bz == nil {
return params
func (k Keeper) GetParams(ctx context.Context) (params types.Params, err error) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(types.ParamsKey)
if bz == nil || err != nil {
return params, err
}
k.cdc.MustUnmarshal(bz, &params)
return params
err = k.cdc.Unmarshal(bz, &params)
return params, err
}
// SetParams sets the distribution parameters.
// CONTRACT: This method performs no validation of the parameters.
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
store := ctx.KVStore(k.storeKey)
func (k Keeper) SetParams(ctx context.Context, params types.Params) error {
store := k.storeService.OpenKVStore(ctx)
bz, err := k.cdc.Marshal(&params)
if err != nil {
return err
}
store.Set(types.ParamsKey, bz)
return nil
return store.Set(types.ParamsKey, bz)
}
// GetCommunityTax returns the current distribution community tax.
func (k Keeper) GetCommunityTax(ctx sdk.Context) math.LegacyDec {
return k.GetParams(ctx).CommunityTax
func (k Keeper) GetCommunityTax(ctx context.Context) (math.LegacyDec, error) {
params, err := k.GetParams(ctx)
if err != nil {
return math.LegacyDec{}, err
}
return params.CommunityTax, nil
}
// GetWithdrawAddrEnabled returns the current distribution withdraw address
// enabled parameter.
func (k Keeper) GetWithdrawAddrEnabled(ctx sdk.Context) (enabled bool) {
return k.GetParams(ctx).WithdrawAddrEnabled
func (k Keeper) GetWithdrawAddrEnabled(ctx context.Context) (enabled bool, err error) {
params, err := k.GetParams(ctx)
if err != nil {
return false, err
}
return params.WithdrawAddrEnabled, nil
}

View File

@ -1,40 +1,44 @@
package keeper
import (
"context"
"errors"
gogotypes "github.com/cosmos/gogoproto/types"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// get the delegator withdraw address, defaulting to the delegator address
func (k Keeper) GetDelegatorWithdrawAddr(ctx sdk.Context, delAddr sdk.AccAddress) sdk.AccAddress {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetDelegatorWithdrawAddrKey(delAddr))
func (k Keeper) GetDelegatorWithdrawAddr(ctx context.Context, delAddr sdk.AccAddress) (sdk.AccAddress, error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetDelegatorWithdrawAddrKey(delAddr))
if b == nil {
return delAddr
return delAddr, err
}
return sdk.AccAddress(b)
return sdk.AccAddress(b), nil
}
// set the delegator withdraw address
func (k Keeper) SetDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAddr sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Set(types.GetDelegatorWithdrawAddrKey(delAddr), withdrawAddr.Bytes())
func (k Keeper) SetDelegatorWithdrawAddr(ctx context.Context, delAddr, withdrawAddr sdk.AccAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Set(types.GetDelegatorWithdrawAddrKey(delAddr), withdrawAddr.Bytes())
}
// delete a delegator withdraw addr
func (k Keeper) DeleteDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAddr sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetDelegatorWithdrawAddrKey(delAddr))
func (k Keeper) DeleteDelegatorWithdrawAddr(ctx context.Context, delAddr, withdrawAddr sdk.AccAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetDelegatorWithdrawAddrKey(delAddr))
}
// iterate over delegator withdraw addrs
func (k Keeper) IterateDelegatorWithdrawAddrs(ctx sdk.Context, handler func(del, addr sdk.AccAddress) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iter := storetypes.KVStorePrefixIterator(store, types.DelegatorWithdrawAddrPrefix)
func (k Keeper) IterateDelegatorWithdrawAddrs(ctx context.Context, handler func(del, addr sdk.AccAddress) (stop bool)) {
store := k.storeService.OpenKVStore(ctx)
iter := storetypes.KVStorePrefixIterator(runtime.KVStoreAdapter(store), types.DelegatorWithdrawAddrPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
addr := sdk.AccAddress(iter.Value())
@ -46,74 +50,98 @@ func (k Keeper) IterateDelegatorWithdrawAddrs(ctx sdk.Context, handler func(del,
}
// get the global fee pool distribution info
func (k Keeper) GetFeePool(ctx sdk.Context) (feePool types.FeePool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.FeePoolKey)
func (k Keeper) GetFeePool(ctx context.Context) (feePool types.FeePool, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.FeePoolKey)
if err != nil {
return
}
if b == nil {
panic("Stored fee pool should not have been nil")
}
k.cdc.MustUnmarshal(b, &feePool)
return
err = k.cdc.Unmarshal(b, &feePool)
return feePool, err
}
// set the global fee pool distribution info
func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&feePool)
store.Set(types.FeePoolKey, b)
func (k Keeper) SetFeePool(ctx context.Context, feePool types.FeePool) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&feePool)
if err != nil {
return err
}
return store.Set(types.FeePoolKey, b)
}
// GetPreviousProposerConsAddr returns the proposer consensus address for the
// current block.
func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) sdk.ConsAddress {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.ProposerKey)
func (k Keeper) GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(types.ProposerKey)
if err != nil {
return nil, err
}
if bz == nil {
panic("previous proposer not set")
return nil, errors.New("previous proposer not set")
}
addrValue := gogotypes.BytesValue{}
k.cdc.MustUnmarshal(bz, &addrValue)
return addrValue.GetValue()
err = k.cdc.Unmarshal(bz, &addrValue)
if err != nil {
return nil, err
}
return addrValue.GetValue(), nil
}
// set the proposer public key for this block
func (k Keeper) SetPreviousProposerConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) SetPreviousProposerConsAddr(ctx context.Context, consAddr sdk.ConsAddress) {
store := k.storeService.OpenKVStore(ctx)
bz := k.cdc.MustMarshal(&gogotypes.BytesValue{Value: consAddr})
store.Set(types.ProposerKey, bz)
}
// get the starting info associated with a delegator
func (k Keeper) GetDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) (period types.DelegatorStartingInfo) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetDelegatorStartingInfoKey(val, del))
k.cdc.MustUnmarshal(b, &period)
return
func (k Keeper) GetDelegatorStartingInfo(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress) (period types.DelegatorStartingInfo, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetDelegatorStartingInfoKey(val, del))
if err != nil {
return
}
err = k.cdc.Unmarshal(b, &period)
return period, err
}
// set the starting info associated with a delegator
func (k Keeper) SetDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress, period types.DelegatorStartingInfo) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&period)
store.Set(types.GetDelegatorStartingInfoKey(val, del), b)
func (k Keeper) SetDelegatorStartingInfo(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress, period types.DelegatorStartingInfo) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&period)
if err != nil {
return err
}
return store.Set(types.GetDelegatorStartingInfoKey(val, del), b)
}
// check existence of the starting info associated with a delegator
func (k Keeper) HasDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) bool {
store := ctx.KVStore(k.storeKey)
func (k Keeper) HasDelegatorStartingInfo(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress) (bool, error) {
store := k.storeService.OpenKVStore(ctx)
return store.Has(types.GetDelegatorStartingInfoKey(val, del))
}
// delete the starting info associated with a delegator
func (k Keeper) DeleteDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetDelegatorStartingInfoKey(val, del))
func (k Keeper) DeleteDelegatorStartingInfo(ctx context.Context, val sdk.ValAddress, del sdk.AccAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetDelegatorStartingInfoKey(val, del))
}
// iterate over delegator starting infos
func (k Keeper) IterateDelegatorStartingInfos(ctx sdk.Context, handler func(val sdk.ValAddress, del sdk.AccAddress, info types.DelegatorStartingInfo) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateDelegatorStartingInfos(ctx context.Context, handler func(val sdk.ValAddress, del sdk.AccAddress, info types.DelegatorStartingInfo) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.DelegatorStartingInfoPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -127,23 +155,31 @@ func (k Keeper) IterateDelegatorStartingInfos(ctx sdk.Context, handler func(val
}
// get historical rewards for a particular period
func (k Keeper) GetValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAddress, period uint64) (rewards types.ValidatorHistoricalRewards) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetValidatorHistoricalRewardsKey(val, period))
k.cdc.MustUnmarshal(b, &rewards)
func (k Keeper) GetValidatorHistoricalRewards(ctx context.Context, val sdk.ValAddress, period uint64) (rewards types.ValidatorHistoricalRewards, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetValidatorHistoricalRewardsKey(val, period))
if err != nil {
return
}
err = k.cdc.Unmarshal(b, &rewards)
return
}
// set historical rewards for a particular period
func (k Keeper) SetValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&rewards)
store.Set(types.GetValidatorHistoricalRewardsKey(val, period), b)
func (k Keeper) SetValidatorHistoricalRewards(ctx context.Context, val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&rewards)
if err != nil {
return err
}
return store.Set(types.GetValidatorHistoricalRewardsKey(val, period), b)
}
// iterate over historical rewards
func (k Keeper) IterateValidatorHistoricalRewards(ctx sdk.Context, handler func(val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateValidatorHistoricalRewards(ctx context.Context, handler func(val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorHistoricalRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -157,14 +193,14 @@ func (k Keeper) IterateValidatorHistoricalRewards(ctx sdk.Context, handler func(
}
// delete a historical reward
func (k Keeper) DeleteValidatorHistoricalReward(ctx sdk.Context, val sdk.ValAddress, period uint64) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetValidatorHistoricalRewardsKey(val, period))
func (k Keeper) DeleteValidatorHistoricalReward(ctx context.Context, val sdk.ValAddress, period uint64) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetValidatorHistoricalRewardsKey(val, period))
}
// delete historical rewards for a validator
func (k Keeper) DeleteValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) DeleteValidatorHistoricalRewards(ctx context.Context, val sdk.ValAddress) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.GetValidatorHistoricalRewardsPrefix(val))
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -173,8 +209,8 @@ func (k Keeper) DeleteValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAdd
}
// delete all historical rewards
func (k Keeper) DeleteAllValidatorHistoricalRewards(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) DeleteAllValidatorHistoricalRewards(ctx context.Context) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorHistoricalRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -183,8 +219,8 @@ func (k Keeper) DeleteAllValidatorHistoricalRewards(ctx sdk.Context) {
}
// historical reference count (used for testcases)
func (k Keeper) GetValidatorHistoricalReferenceCount(ctx sdk.Context) (count uint64) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) GetValidatorHistoricalReferenceCount(ctx context.Context) (count uint64) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorHistoricalRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -196,29 +232,37 @@ func (k Keeper) GetValidatorHistoricalReferenceCount(ctx sdk.Context) (count uin
}
// get current rewards for a validator
func (k Keeper) GetValidatorCurrentRewards(ctx sdk.Context, val sdk.ValAddress) (rewards types.ValidatorCurrentRewards) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetValidatorCurrentRewardsKey(val))
k.cdc.MustUnmarshal(b, &rewards)
func (k Keeper) GetValidatorCurrentRewards(ctx context.Context, val sdk.ValAddress) (rewards types.ValidatorCurrentRewards, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetValidatorCurrentRewardsKey(val))
if err != nil {
return
}
err = k.cdc.Unmarshal(b, &rewards)
return
}
// set current rewards for a validator
func (k Keeper) SetValidatorCurrentRewards(ctx sdk.Context, val sdk.ValAddress, rewards types.ValidatorCurrentRewards) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&rewards)
store.Set(types.GetValidatorCurrentRewardsKey(val), b)
func (k Keeper) SetValidatorCurrentRewards(ctx context.Context, val sdk.ValAddress, rewards types.ValidatorCurrentRewards) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&rewards)
if err != nil {
return err
}
return store.Set(types.GetValidatorCurrentRewardsKey(val), b)
}
// delete current rewards for a validator
func (k Keeper) DeleteValidatorCurrentRewards(ctx sdk.Context, val sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetValidatorCurrentRewardsKey(val))
func (k Keeper) DeleteValidatorCurrentRewards(ctx context.Context, val sdk.ValAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetValidatorCurrentRewardsKey(val))
}
// iterate over current rewards
func (k Keeper) IterateValidatorCurrentRewards(ctx sdk.Context, handler func(val sdk.ValAddress, rewards types.ValidatorCurrentRewards) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateValidatorCurrentRewards(ctx context.Context, handler func(val sdk.ValAddress, rewards types.ValidatorCurrentRewards) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorCurrentRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -232,39 +276,54 @@ func (k Keeper) IterateValidatorCurrentRewards(ctx sdk.Context, handler func(val
}
// get accumulated commission for a validator
func (k Keeper) GetValidatorAccumulatedCommission(ctx sdk.Context, val sdk.ValAddress) (commission types.ValidatorAccumulatedCommission) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetValidatorAccumulatedCommissionKey(val))
if b == nil {
return types.ValidatorAccumulatedCommission{}
func (k Keeper) GetValidatorAccumulatedCommission(ctx context.Context, val sdk.ValAddress) (commission types.ValidatorAccumulatedCommission, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetValidatorAccumulatedCommissionKey(val))
if err != nil {
return types.ValidatorAccumulatedCommission{}, err
}
if b == nil {
return types.ValidatorAccumulatedCommission{}, nil
}
err = k.cdc.Unmarshal(b, &commission)
if err != nil {
return types.ValidatorAccumulatedCommission{}, err
}
k.cdc.MustUnmarshal(b, &commission)
return
}
// set accumulated commission for a validator
func (k Keeper) SetValidatorAccumulatedCommission(ctx sdk.Context, val sdk.ValAddress, commission types.ValidatorAccumulatedCommission) {
var bz []byte
func (k Keeper) SetValidatorAccumulatedCommission(ctx context.Context, val sdk.ValAddress, commission types.ValidatorAccumulatedCommission) error {
var (
bz []byte
err error
)
store := ctx.KVStore(k.storeKey)
store := k.storeService.OpenKVStore(ctx)
if commission.Commission.IsZero() {
bz = k.cdc.MustMarshal(&types.ValidatorAccumulatedCommission{})
bz, err = k.cdc.Marshal(&types.ValidatorAccumulatedCommission{})
} else {
bz = k.cdc.MustMarshal(&commission)
bz, err = k.cdc.Marshal(&commission)
}
store.Set(types.GetValidatorAccumulatedCommissionKey(val), bz)
if err != nil {
return err
}
return store.Set(types.GetValidatorAccumulatedCommissionKey(val), bz)
}
// delete accumulated commission for a validator
func (k Keeper) DeleteValidatorAccumulatedCommission(ctx sdk.Context, val sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetValidatorAccumulatedCommissionKey(val))
func (k Keeper) DeleteValidatorAccumulatedCommission(ctx context.Context, val sdk.ValAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetValidatorAccumulatedCommissionKey(val))
}
// iterate over accumulated commissions
func (k Keeper) IterateValidatorAccumulatedCommissions(ctx sdk.Context, handler func(val sdk.ValAddress, commission types.ValidatorAccumulatedCommission) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateValidatorAccumulatedCommissions(ctx context.Context, handler func(val sdk.ValAddress, commission types.ValidatorAccumulatedCommission) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorAccumulatedCommissionPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -278,29 +337,35 @@ func (k Keeper) IterateValidatorAccumulatedCommissions(ctx sdk.Context, handler
}
// get validator outstanding rewards
func (k Keeper) GetValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress) (rewards types.ValidatorOutstandingRewards) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetValidatorOutstandingRewardsKey(val))
k.cdc.MustUnmarshal(bz, &rewards)
func (k Keeper) GetValidatorOutstandingRewards(ctx context.Context, val sdk.ValAddress) (rewards types.ValidatorOutstandingRewards, err error) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(types.GetValidatorOutstandingRewardsKey(val))
if err != nil {
return
}
err = k.cdc.Unmarshal(bz, &rewards)
return
}
// set validator outstanding rewards
func (k Keeper) SetValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&rewards)
store.Set(types.GetValidatorOutstandingRewardsKey(val), b)
func (k Keeper) SetValidatorOutstandingRewards(ctx context.Context, val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&rewards)
if err != nil {
return err
}
return store.Set(types.GetValidatorOutstandingRewardsKey(val), b)
}
// delete validator outstanding rewards
func (k Keeper) DeleteValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetValidatorOutstandingRewardsKey(val))
func (k Keeper) DeleteValidatorOutstandingRewards(ctx context.Context, val sdk.ValAddress) error {
store := k.storeService.OpenKVStore(ctx)
return store.Delete(types.GetValidatorOutstandingRewardsKey(val))
}
// iterate validator outstanding rewards
func (k Keeper) IterateValidatorOutstandingRewards(ctx sdk.Context, handler func(val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateValidatorOutstandingRewards(ctx context.Context, handler func(val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorOutstandingRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -314,28 +379,41 @@ func (k Keeper) IterateValidatorOutstandingRewards(ctx sdk.Context, handler func
}
// get slash event for height
func (k Keeper) GetValidatorSlashEvent(ctx sdk.Context, val sdk.ValAddress, height, period uint64) (event types.ValidatorSlashEvent, found bool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.GetValidatorSlashEventKey(val, height, period))
if b == nil {
return types.ValidatorSlashEvent{}, false
func (k Keeper) GetValidatorSlashEvent(ctx context.Context, val sdk.ValAddress, height, period uint64) (event types.ValidatorSlashEvent, found bool, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetValidatorSlashEventKey(val, height, period))
if err != nil {
return types.ValidatorSlashEvent{}, false, err
}
k.cdc.MustUnmarshal(b, &event)
return event, true
if b == nil {
return types.ValidatorSlashEvent{}, false, nil
}
err = k.cdc.Unmarshal(b, &event)
if err != nil {
return types.ValidatorSlashEvent{}, false, err
}
return event, true, nil
}
// set slash event for height
func (k Keeper) SetValidatorSlashEvent(ctx sdk.Context, val sdk.ValAddress, height, period uint64, event types.ValidatorSlashEvent) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshal(&event)
store.Set(types.GetValidatorSlashEventKey(val, height, period), b)
func (k Keeper) SetValidatorSlashEvent(ctx context.Context, val sdk.ValAddress, height, period uint64, event types.ValidatorSlashEvent) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&event)
if err != nil {
return err
}
return store.Set(types.GetValidatorSlashEventKey(val, height, period), b)
}
// iterate over slash events between heights, inclusive
func (k Keeper) IterateValidatorSlashEventsBetween(ctx sdk.Context, val sdk.ValAddress, startingHeight, endingHeight uint64,
func (k Keeper) IterateValidatorSlashEventsBetween(ctx context.Context, val sdk.ValAddress, startingHeight, endingHeight uint64,
handler func(height uint64, event types.ValidatorSlashEvent) (stop bool),
) {
store := ctx.KVStore(k.storeKey)
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := store.Iterator(
types.GetValidatorSlashEventKeyPrefix(val, startingHeight),
types.GetValidatorSlashEventKeyPrefix(val, endingHeight+1),
@ -352,8 +430,8 @@ func (k Keeper) IterateValidatorSlashEventsBetween(ctx sdk.Context, val sdk.ValA
}
// iterate over all slash events
func (k Keeper) IterateValidatorSlashEvents(ctx sdk.Context, handler func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool)) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) IterateValidatorSlashEvents(ctx context.Context, handler func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorSlashEventPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -367,8 +445,8 @@ func (k Keeper) IterateValidatorSlashEvents(ctx sdk.Context, handler func(val sd
}
// delete slash events for a particular validator
func (k Keeper) DeleteValidatorSlashEvents(ctx sdk.Context, val sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) DeleteValidatorSlashEvents(ctx context.Context, val sdk.ValAddress) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.GetValidatorSlashEventPrefix(val))
defer iter.Close()
for ; iter.Valid(); iter.Next() {
@ -377,8 +455,8 @@ func (k Keeper) DeleteValidatorSlashEvents(ctx sdk.Context, val sdk.ValAddress)
}
// delete all slash events
func (k Keeper) DeleteAllValidatorSlashEvents(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
func (k Keeper) DeleteAllValidatorSlashEvents(ctx context.Context) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorSlashEventPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {

View File

@ -1,9 +1,11 @@
package keeper
import (
"context"
"fmt"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
@ -11,24 +13,37 @@ import (
)
// initialize rewards for a new validator
func (k Keeper) initializeValidator(ctx sdk.Context, val stakingtypes.ValidatorI) {
func (k Keeper) initializeValidator(ctx context.Context, val stakingtypes.ValidatorI) error {
// set initial historical rewards (period 0) with reference count of 1
k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), 0, types.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1))
err := k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), 0, types.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1))
if err != nil {
return err
}
// set current rewards (starting at period 1)
k.SetValidatorCurrentRewards(ctx, val.GetOperator(), types.NewValidatorCurrentRewards(sdk.DecCoins{}, 1))
err = k.SetValidatorCurrentRewards(ctx, val.GetOperator(), types.NewValidatorCurrentRewards(sdk.DecCoins{}, 1))
if err != nil {
return err
}
// set accumulated commission
k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), types.InitialValidatorAccumulatedCommission())
err = k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), types.InitialValidatorAccumulatedCommission())
if err != nil {
return err
}
// set outstanding rewards
k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), types.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}})
err = k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), types.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}})
return err
}
// increment validator period, returning the period just ended
func (k Keeper) IncrementValidatorPeriod(ctx sdk.Context, val stakingtypes.ValidatorI) uint64 {
func (k Keeper) IncrementValidatorPeriod(ctx context.Context, val stakingtypes.ValidatorI) (uint64, error) {
// fetch current rewards
rewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
rewards, err := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
if err != nil {
return 0, err
}
// calculate current ratio
var current sdk.DecCoins
@ -36,12 +51,27 @@ func (k Keeper) IncrementValidatorPeriod(ctx sdk.Context, val stakingtypes.Valid
// can't calculate ratio for zero-token validators
// ergo we instead add to the community pool
feePool := k.GetFeePool(ctx)
outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
feePool, err := k.GetFeePool(ctx)
if err != nil {
return 0, err
}
outstanding, err := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
if err != nil {
return 0, err
}
feePool.CommunityPool = feePool.CommunityPool.Add(rewards.Rewards...)
outstanding.Rewards = outstanding.GetRewards().Sub(rewards.Rewards)
k.SetFeePool(ctx, feePool)
k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
err = k.SetFeePool(ctx, feePool)
if err != nil {
return 0, err
}
err = k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
if err != nil {
return 0, err
}
current = sdk.DecCoins{}
} else {
@ -50,59 +80,84 @@ func (k Keeper) IncrementValidatorPeriod(ctx sdk.Context, val stakingtypes.Valid
}
// fetch historical rewards for last period
historical := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period-1).CumulativeRewardRatio
historical, err := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period-1)
if err != nil {
return 0, err
}
cumRewardRatio := historical.CumulativeRewardRatio
// decrement reference count
k.decrementReferenceCount(ctx, val.GetOperator(), rewards.Period-1)
err = k.decrementReferenceCount(ctx, val.GetOperator(), rewards.Period-1)
if err != nil {
return 0, err
}
// set new historical rewards with reference count of 1
k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period, types.NewValidatorHistoricalRewards(historical.Add(current...), 1))
err = k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period, types.NewValidatorHistoricalRewards(cumRewardRatio.Add(current...), 1))
if err != nil {
return 0, err
}
// set current rewards, incrementing period by 1
k.SetValidatorCurrentRewards(ctx, val.GetOperator(), types.NewValidatorCurrentRewards(sdk.DecCoins{}, rewards.Period+1))
err = k.SetValidatorCurrentRewards(ctx, val.GetOperator(), types.NewValidatorCurrentRewards(sdk.DecCoins{}, rewards.Period+1))
if err != nil {
return 0, err
}
return rewards.Period
return rewards.Period, nil
}
// increment the reference count for a historical rewards value
func (k Keeper) incrementReferenceCount(ctx sdk.Context, valAddr sdk.ValAddress, period uint64) {
historical := k.GetValidatorHistoricalRewards(ctx, valAddr, period)
func (k Keeper) incrementReferenceCount(ctx context.Context, valAddr sdk.ValAddress, period uint64) error {
historical, err := k.GetValidatorHistoricalRewards(ctx, valAddr, period)
if err != nil {
return err
}
if historical.ReferenceCount > 2 {
panic("reference count should never exceed 2")
}
historical.ReferenceCount++
k.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
return k.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
}
// decrement the reference count for a historical rewards value, and delete if zero references remain
func (k Keeper) decrementReferenceCount(ctx sdk.Context, valAddr sdk.ValAddress, period uint64) {
historical := k.GetValidatorHistoricalRewards(ctx, valAddr, period)
func (k Keeper) decrementReferenceCount(ctx context.Context, valAddr sdk.ValAddress, period uint64) error {
historical, err := k.GetValidatorHistoricalRewards(ctx, valAddr, period)
if err != nil {
return err
}
if historical.ReferenceCount == 0 {
panic("cannot set negative reference count")
}
historical.ReferenceCount--
if historical.ReferenceCount == 0 {
k.DeleteValidatorHistoricalReward(ctx, valAddr, period)
} else {
k.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
return k.DeleteValidatorHistoricalReward(ctx, valAddr, period)
}
return k.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
}
func (k Keeper) updateValidatorSlashFraction(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
func (k Keeper) updateValidatorSlashFraction(ctx context.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error {
if fraction.GT(math.LegacyOneDec()) || fraction.IsNegative() {
panic(fmt.Sprintf("fraction must be >=0 and <=1, current fraction: %v", fraction))
}
val := k.stakingKeeper.Validator(ctx, valAddr)
sdkCtx := sdk.UnwrapSDKContext(ctx)
val := k.stakingKeeper.Validator(sdkCtx, valAddr)
// increment current period
newPeriod := k.IncrementValidatorPeriod(ctx, val)
newPeriod, err := k.IncrementValidatorPeriod(ctx, val)
if err != nil {
return err
}
// increment reference count on period we need to track
k.incrementReferenceCount(ctx, valAddr, newPeriod)
slashEvent := types.NewValidatorSlashEvent(newPeriod, fraction)
height := uint64(ctx.BlockHeight())
height := uint64(sdkCtx.BlockHeight())
k.SetValidatorSlashEvent(ctx, valAddr, height, newPeriod, slashEvent)
return k.SetValidatorSlashEvent(ctx, valAddr, height, newPeriod, slashEvent)
}

View File

@ -1,8 +1,9 @@
package v2
import (
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
v1 "github.com/cosmos/cosmos-sdk/x/distribution/migrations/v1"
)
@ -11,8 +12,8 @@ import (
// migration includes:
//
// - Change addresses to be length-prefixed.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey) error {
store := ctx.KVStore(storeKey)
func MigrateStore(ctx sdk.Context, storeService store.KVStoreService) error {
store := runtime.KVStoreAdapter(storeService.OpenKVStore(ctx))
MigratePrefixAddress(store, v1.ValidatorOutstandingRewardsPrefix)
MigratePrefixAddress(store, v1.DelegatorWithdrawAddrPrefix)
MigratePrefixAddressAddress(store, v1.DelegatorStartingInfoPrefix)

View File

@ -8,6 +8,7 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -18,6 +19,7 @@ import (
func TestStoreMigration(t *testing.T) {
distributionKey := storetypes.NewKVStoreKey("distribution")
storeService := runtime.NewKVStoreService(distributionKey)
ctx := testutil.DefaultContext(distributionKey, storetypes.NewTransientStoreKey("transient_test"))
store := ctx.KVStore(distributionKey)
@ -85,7 +87,7 @@ func TestStoreMigration(t *testing.T) {
}
// Run migrations.
err := v2.MigrateStore(ctx, distributionKey)
err := v2.MigrateStore(ctx, storeService)
require.NoError(t, err)
// Make sure the new keys are set and old keys are deleted.

View File

@ -1,7 +1,7 @@
package v3
import (
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/core/store"
sdkmath "cosmossdk.io/math"
@ -21,8 +21,8 @@ var ParamsKey = []byte{0x09}
// version 3. Specifically, it takes the parameters that are currently stored
// and managed by the x/params module and stores them directly into the x/distribution
// module state.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, legacySubspace exported.Subspace, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)
func MigrateStore(ctx sdk.Context, storeService store.KVStoreService, legacySubspace exported.Subspace, cdc codec.BinaryCodec) error {
store := storeService.OpenKVStore(ctx)
var currParams types.Params
legacySubspace.GetParamSet(ctx, &currParams)
@ -39,7 +39,5 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, legacySubspace
return err
}
store.Set(ParamsKey, bz)
return nil
return store.Set(ParamsKey, bz)
}

View File

@ -7,6 +7,7 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
@ -31,12 +32,13 @@ func (ms mockSubspace) GetParamSet(ctx sdk.Context, ps exported.ParamSet) {
func TestMigrate(t *testing.T) {
cdc := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}).Codec
storeKey := storetypes.NewKVStoreKey(v3.ModuleName)
storeService := runtime.NewKVStoreService(storeKey)
tKey := storetypes.NewTransientStoreKey("transient_test")
ctx := testutil.DefaultContext(storeKey, tKey)
store := ctx.KVStore(storeKey)
legacySubspace := newMockSubspace(types.DefaultParams())
require.NoError(t, v3.MigrateStore(ctx, storeKey, legacySubspace, cdc))
require.NoError(t, v3.MigrateStore(ctx, storeService, legacySubspace, cdc))
var res types.Params
bz := store.Get(v3.ParamsKey)

View File

@ -14,7 +14,7 @@ import (
"cosmossdk.io/core/appmodule"
"cosmossdk.io/depinject"
store "cosmossdk.io/store/types"
"cosmossdk.io/core/store"
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
@ -221,9 +221,9 @@ func init() {
type ModuleInputs struct {
depinject.In
Config *modulev1.Module
Key *store.KVStoreKey
Cdc codec.Codec
Config *modulev1.Module
StoreService store.KVStoreService
Cdc codec.Codec
AccountKeeper types.AccountKeeper
BankKeeper types.BankKeeper
@ -255,7 +255,7 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
k := keeper.NewKeeper(
in.Cdc,
in.Key,
in.StoreService,
in.AccountKeeper,
in.BankKeeper,
in.StakingKeeper,

View File

@ -91,7 +91,12 @@ func SimulateMsgSetWithdrawAddress(txConfig client.TxConfig, ak types.AccountKee
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
if !k.GetWithdrawAddrEnabled(ctx) {
isWithdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "error getting params"), nil, err
}
if !isWithdrawAddrEnabled {
return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "withdrawal is not enabled"), nil, nil
}
@ -174,7 +179,11 @@ func SimulateMsgWithdrawValidatorCommission(txConfig client.TxConfig, ak types.A
return simtypes.NoOpMsg(types.ModuleName, msgType, "random validator is not ok"), nil, nil
}
commission := k.GetValidatorAccumulatedCommission(ctx, validator.GetOperator())
commission, err := k.GetValidatorAccumulatedCommission(ctx, validator.GetOperator())
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msgType, "error getting validator commission"), nil, err
}
if commission.Commission.IsZero() {
return simtypes.NoOpMsg(types.ModuleName, msgType, "validator commission is zero"), nil, nil
}

View File

@ -37,5 +37,5 @@ type StakingKeeper interface {
// DistributionKeeper defines the expected distribution keeper
type DistributionKeeper interface {
FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error
FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error
}

View File

@ -1089,7 +1089,7 @@ func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder {
}
// FundCommunityPool mocks base method.
func (m *MockDistributionKeeper) FundCommunityPool(ctx types.Context, amount types.Coins, sender types.AccAddress) error {
func (m *MockDistributionKeeper) FundCommunityPool(ctx context.Context, amount types.Coins, sender types.AccAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FundCommunityPool", ctx, amount, sender)
ret0, _ := ret[0].(error)

View File

@ -32,7 +32,7 @@ type StakingKeeper interface {
// DistributionKeeper defines the expected distribution keeper (noalias)
type DistributionKeeper interface {
FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error
FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error
}
// AccountKeeper defines the expected account keeper (noalias)

View File

@ -334,7 +334,7 @@ func (mr *MockStakingKeeperMockRecorder) MaxValidators(arg0 interface{}) *gomock
}
// Slash mocks base method.
func (m *MockStakingKeeper) Slash(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 types.Dec) math.Int {
func (m *MockStakingKeeper) Slash(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec) math.Int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Slash", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(math.Int)
@ -348,7 +348,7 @@ func (mr *MockStakingKeeperMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 inte
}
// SlashWithInfractionReason mocks base method.
func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 types.Dec, arg5 types1.Infraction) math.Int {
func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec, arg5 types1.Infraction) math.Int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SlashWithInfractionReason", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(math.Int)
@ -551,7 +551,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeValidatorModified(ctx, valAddr int
}
// BeforeValidatorSlashed mocks base method.
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx types.Context, valAddr types.ValAddress, fraction types.Dec) error {
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx types.Context, valAddr types.ValAddress, fraction math.LegacyDec) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeValidatorSlashed", ctx, valAddr, fraction)
ret0, _ := ret[0].(error)

View File

@ -38,7 +38,7 @@ func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder {
}
// GetFeePoolCommunityCoins mocks base method.
func (m *MockDistributionKeeper) GetFeePoolCommunityCoins(ctx types.Context) types.DecCoins {
func (m *MockDistributionKeeper) GetFeePoolCommunityCoins(ctx context.Context) types.DecCoins {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetFeePoolCommunityCoins", ctx)
ret0, _ := ret[0].(types.DecCoins)
@ -52,7 +52,7 @@ func (mr *MockDistributionKeeperMockRecorder) GetFeePoolCommunityCoins(ctx inter
}
// GetValidatorOutstandingRewardsCoins mocks base method.
func (m *MockDistributionKeeper) GetValidatorOutstandingRewardsCoins(ctx types.Context, val types.ValAddress) types.DecCoins {
func (m *MockDistributionKeeper) GetValidatorOutstandingRewardsCoins(ctx context.Context, val types.ValAddress) types.DecCoins {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetValidatorOutstandingRewardsCoins", ctx, val)
ret0, _ := ret[0].(types.DecCoins)
@ -433,7 +433,7 @@ func (mr *MockValidatorSetMockRecorder) MaxValidators(arg0 interface{}) *gomock.
}
// Slash mocks base method.
func (m *MockValidatorSet) Slash(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 types.Dec) math.Int {
func (m *MockValidatorSet) Slash(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec) math.Int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Slash", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(math.Int)
@ -447,7 +447,7 @@ func (mr *MockValidatorSetMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 inter
}
// SlashWithInfractionReason mocks base method.
func (m *MockValidatorSet) SlashWithInfractionReason(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 types.Dec, arg5 types0.Infraction) math.Int {
func (m *MockValidatorSet) SlashWithInfractionReason(arg0 types.Context, arg1 types.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec, arg5 types0.Infraction) math.Int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SlashWithInfractionReason", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(math.Int)
@ -741,7 +741,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeValidatorModified(ctx, valAddr int
}
// BeforeValidatorSlashed mocks base method.
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx types.Context, valAddr types.ValAddress, fraction types.Dec) error {
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx types.Context, valAddr types.ValAddress, fraction math.LegacyDec) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeValidatorSlashed", ctx, valAddr, fraction)
ret0, _ := ret[0].(error)

View File

@ -11,8 +11,8 @@ import (
// DistributionKeeper expected distribution keeper (noalias)
type DistributionKeeper interface {
GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins
GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.ValAddress) sdk.DecCoins
GetFeePoolCommunityCoins(ctx context.Context) sdk.DecCoins
GetValidatorOutstandingRewardsCoins(ctx context.Context, val sdk.ValAddress) sdk.DecCoins
}
// AccountKeeper defines the expected account keeper (noalias)