Skip to content

Commit

Permalink
A few bug fixes; simulation testing; testcases
Browse files Browse the repository at this point in the history
  • Loading branch information
cwgoes committed Jan 5, 2019
1 parent db781b8 commit d5404bf
Show file tree
Hide file tree
Showing 18 changed files with 235 additions and 52 deletions.
10 changes: 9 additions & 1 deletion cmd/gaia/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b

app.QueryRouter().
AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)).
AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)).
AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)).
AddRoute(stake.QuerierRoute, stake.NewQuerier(app.stakeKeeper, app.cdc))

// initialize BaseApp
Expand Down Expand Up @@ -370,3 +370,11 @@ func (h StakingHooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddres
h.dh.OnDelegationRemoved(ctx, delAddr, valAddr)
h.sh.OnDelegationRemoved(ctx, delAddr, valAddr)
}
func (h StakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.AfterDelegationModified(ctx, delAddr, valAddr)
h.sh.AfterDelegationModified(ctx, delAddr, valAddr)
}
func (h StakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
h.dh.BeforeValidatorSlashed(ctx, valAddr, fraction)
h.sh.BeforeValidatorSlashed(ctx, valAddr, fraction)
}
5 changes: 5 additions & 0 deletions docs/examples/democoin/mock/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func (v Validator) GetMoniker() string {
return ""
}

// Implements sdk.Validator
func (v Validator) GetDelegatorShareExRate() sdk.Dec {
return sdk.ZeroDec()
}

// Implements sdk.Validator
type ValidatorSet struct {
Validators []Validator
Expand Down
5 changes: 4 additions & 1 deletion types/stake.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ type Validator interface {
GetPower() Int // validation power
GetTokens() Int // validation tokens
GetCommission() Dec // validator commission rate
GetDelegatorShares() Dec // Total out standing delegator shares
GetDelegatorShares() Dec // total outstanding delegator shares
GetBondHeight() int64 // height in which the validator became active
GetDelegatorShareExRate() Dec // tokens per delegator share exchange rate
}

// validator which fulfills abci validator interface for use in Tendermint
Expand Down Expand Up @@ -126,4 +127,6 @@ type StakingHooks interface {
OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is created
OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation's shares are modified
OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is removed
AfterDelegationModified(ctx Context, delAddr AccAddress, valAddr ValAddress)
BeforeValidatorSlashed(ctx Context, valAddr ValAddress, fraction Dec)
}
3 changes: 2 additions & 1 deletion x/distribution/abci_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
}
}

// TODO This is Tendermint-dependent. ref https://github.com/cosmos/cosmos-sdk/issues/3095
// TODO this is Tendermint-dependent
// ref https://github.com/cosmos/cosmos-sdk/issues/3095
if ctx.BlockHeight() > 1 {
previousProposer := k.GetPreviousProposerConsAddr(ctx)
k.AllocateTokens(ctx, sumPrecommitPower, totalPower, previousProposer, req.LastCommitInfo.GetVotes())
Expand Down
24 changes: 13 additions & 11 deletions x/distribution/keeper/allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,23 @@ import (
// allocate fees handles distribution of the collected fees
func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower int64, totalPower int64, proposer sdk.ConsAddress, votes []abci.VoteInfo) {

// Fetch collected fees & fee pool
// fetch collected fees & fee pool
feesCollectedInt := k.feeCollectionKeeper.GetCollectedFees(ctx)
feesCollected := sdk.NewDecCoins(feesCollectedInt)
feePool := k.GetFeePool(ctx)

// Clear collected fees, which will now be distributed
// clear collected fees, which will now be distributed
k.feeCollectionKeeper.ClearCollectedFees(ctx)

// Update outstanding rewards
outstanding := k.GetOutstandingRewards(ctx)
outstanding = outstanding.Plus(feesCollected)
k.SetOutstandingRewards(ctx, outstanding)

// Temporary workaround to keep CanWithdrawInvariant happy.
// General discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
// temporary workaround to keep CanWithdrawInvariant happy.
// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
if totalPower == 0 {
feePool.CommunityPool = feePool.CommunityPool.Plus(feesCollected)
k.SetFeePool(ctx, feePool)
return
}

// Calculate fraction votes
// calculate fraction votes
fractionVotes := sdk.NewDec(sumPrecommitPower).Quo(sdk.NewDec(totalPower))

// calculate proposer reward
Expand All @@ -52,7 +47,8 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower int64, totalPo
for _, vote := range votes {
validator := k.stakeKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)

// TODO Likely we should only reward validators who actually signed the block.
// TODO likely we should only reward validators who actually signed the block.
// ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701
powerFraction := sdk.NewDec(vote.Validator.Power).Quo(sdk.NewDec(totalPower))
reward := feesCollected.MulDec(voteMultiplier).MulDec(powerFraction)
k.AllocateTokensToValidator(ctx, validator, reward)
Expand All @@ -61,6 +57,12 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower int64, totalPo

// allocate community funding
feePool.CommunityPool = feePool.CommunityPool.Plus(remaining)
k.SetFeePool(ctx, feePool)

// update outstanding rewards
outstanding := k.GetOutstandingRewards(ctx)
outstanding = outstanding.Plus(feesCollected.Minus(remaining))
k.SetOutstandingRewards(ctx, outstanding)

}

Expand Down
74 changes: 61 additions & 13 deletions x/distribution/keeper/calculation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (
)

func (k Keeper) initializeValidator(ctx sdk.Context, val sdk.Validator) {
// Set initial historical rewards (period 0)
// set initial historical rewards (period 0)
k.setValidatorHistoricalRewards(ctx, val.GetOperator(), 0, types.ValidatorHistoricalRewards{})

// Set current rewards (starting at period 1)
// set current rewards (starting at period 1)
k.setValidatorCurrentRewards(ctx, val.GetOperator(), types.ValidatorCurrentRewards{
Rewards: sdk.DecCoins{},
Period: 1,
})

// Set accumulated commission
// set accumulated commission
k.setValidatorAccumulatedCommission(ctx, val.GetOperator(), types.ValidatorAccumulatedCommission{})
}

Expand All @@ -28,8 +28,8 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val sdk.Validator) uin
var current sdk.DecCoins
if val.GetPower().IsZero() {
// this can happen after redelegations are slashed
// TODO add to the community pool?
current = sdk.DecCoins{}
// TODO: Add to the community pool?
} else {
current = rewards.Rewards.QuoDec(sdk.NewDecFromInt(val.GetPower()))
}
Expand All @@ -52,33 +52,81 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val sdk.Validator) uin

func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) {
period := k.GetValidatorCurrentRewards(ctx, val).Period
validator := k.stakeKeeper.Validator(ctx, val)
delegation := k.stakeKeeper.Delegation(ctx, del, val)
// TODO need to make sure this truncates instead of rounding
stake := delegation.GetShares().Mul(validator.GetDelegatorShareExRate())
k.setDelegatorStartingInfo(ctx, val, del, types.DelegatorStartingInfo{
PreviousPeriod: period,
Stake: sdk.ZeroInt(),
PreviousPeriod: period - 1,
Stake: stake,
Height: uint64(ctx.BlockHeight()),
})
}

func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val sdk.Validator, startingPeriod uint64, endingPeriod uint64, stake sdk.Dec) (rewards sdk.DecCoins) {
if startingPeriod > endingPeriod {
panic("ending period must be after starting period")
} else if startingPeriod == endingPeriod {
return
}
starting := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
ending := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
difference := ending.Minus(starting)
rewards = difference.MulDec(stake)
return
}

func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val sdk.Validator, del sdk.Delegation) sdk.Error {
endedPeriod := k.incrementValidatorPeriod(ctx, val)
present := k.GetValidatorHistoricalRewards(ctx, del.GetValidatorAddr(), endedPeriod)

endingPeriod := k.incrementValidatorPeriod(ctx, val)

startingInfo := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
historical := k.GetValidatorHistoricalRewards(ctx, del.GetValidatorAddr(), startingInfo.PreviousPeriod)
difference := present.Minus(historical)
rewards := difference.MulDec(sdk.NewDecFromInt(startingInfo.Stake))
startingPeriod := startingInfo.PreviousPeriod
stake := startingInfo.Stake
rewards := sdk.DecCoins{}

// iterate through slashes and withdraw with calculated stake for sub-intervals
startingHeight := startingInfo.Height + 1
endingHeight := uint64(ctx.BlockHeight())
k.IterateValidatorSlashFractions(ctx, del.GetValidatorAddr(), startingHeight, endingHeight, func(height uint64, fraction types.ValidatorSlashFraction) (stop bool) {
stake = stake.Mul(sdk.OneDec().Sub(fraction.Fraction))
endingPeriod := fraction.ValidatorPeriod
rewards = rewards.Plus(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake))
startingPeriod = endingPeriod
return false
})

// Truncate coins, return remainder to community pool
// calculate rewards for final period
rewards = rewards.Plus(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake))

// truncate coins, return remainder to community pool
coins, remainder := rewards.TruncateDecimal()
outstanding := k.GetOutstandingRewards(ctx)
k.SetOutstandingRewards(ctx, outstanding.Minus(rewards))
feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Plus(remainder)
k.SetFeePool(ctx, feePool)

// Add coins to user account
// add coins to user account
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr())
if _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil {
return err
}

return nil
}

func (k Keeper) updateValidatorSlashFraction(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
height := uint64(ctx.BlockHeight())
currentFraction := sdk.ZeroDec()
currentPeriod := k.GetValidatorCurrentRewards(ctx, valAddr).Period
current, found := k.GetValidatorSlashFraction(ctx, valAddr, height)
if found {
currentFraction = current.Fraction
}
updatedFraction := sdk.OneDec().Sub(sdk.OneDec().Sub(currentFraction).Mul(sdk.OneDec().Sub(fraction)))
k.setValidatorSlashFraction(ctx, valAddr, height, types.ValidatorSlashFraction{
ValidatorPeriod: currentPeriod,
Fraction: updatedFraction,
})
}
16 changes: 10 additions & 6 deletions x/distribution/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.initializeValidator(ctx, val)
}
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
val := h.k.stakeKeeper.Validator(ctx, valAddr)
// increment period
h.k.incrementValidatorPeriod(ctx, val)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
}
Expand All @@ -28,9 +31,6 @@ func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valA

// increment period
h.k.incrementValidatorPeriod(ctx, val)

// create new delegation period record
h.k.initializeDelegation(ctx, valAddr, delAddr)
}
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
val := h.k.stakeKeeper.Validator(ctx, valAddr)
Expand All @@ -40,9 +40,6 @@ func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddres
if err := h.k.withdrawDelegationRewards(ctx, val, del); err != nil {
panic(err)
}

// create new delegation period record
h.k.initializeDelegation(ctx, valAddr, delAddr)
}
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
val := h.k.stakeKeeper.Validator(ctx, valAddr)
Expand All @@ -53,9 +50,16 @@ func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valA
panic(err)
}
}
func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
// create new delegation period record
h.k.initializeDelegation(ctx, valAddr, delAddr)
}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
}
func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
}
func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
}
func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
h.k.updateValidatorSlashFraction(ctx, valAddr, fraction)
}
17 changes: 17 additions & 0 deletions x/distribution/keeper/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var (
ValidatorHistoricalRewardsPrefix = []byte{0x05} // key for historical validators rewards / stake
ValidatorCurrentRewardsPrefix = []byte{0x06} // key for current validator rewards
ValidatorAccumulatedCommissionPrefix = []byte{0x07} // key for accumulated validator commission
ValidatorSlashFractionPrefix = []byte{0x08} // key for validator slash fraction

ParamStoreKeyCommunityTax = []byte("communitytax")
ParamStoreKeyBaseProposerReward = []byte("baseproposerreward")
Expand All @@ -42,6 +43,15 @@ func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) {
return sdk.AccAddress(addr)
}

// gets the height from a validator's slash fraction key
func GetValidatorSlashFractionHeight(key []byte) (height uint64) {
b := key[1+sdk.AddrLen:]
if len(b) != 8 {
panic("unexpected key length")
}
return binary.LittleEndian.Uint64(b)
}

// gets the key for a delegator's starting info
func GetDelegatorStartingInfoKey(v sdk.ValAddress, d sdk.AccAddress) []byte {
return append(append(DelegatorStartingInfoPrefix, v.Bytes()...), d.Bytes()...)
Expand All @@ -63,3 +73,10 @@ func GetValidatorCurrentRewardsKey(v sdk.ValAddress) []byte {
func GetValidatorAccumulatedCommissionKey(v sdk.ValAddress) []byte {
return append(ValidatorAccumulatedCommissionPrefix, v.Bytes()...)
}

// gets the key for a validator's slash fraction
func GetValidatorSlashFractionKey(v sdk.ValAddress, height uint64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, height)
return append(append(ValidatorSlashFractionPrefix, v.Bytes()...), b...)
}
8 changes: 4 additions & 4 deletions x/distribution/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/params"
)

// Type declaration for parameters
// type declaration for parameters
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
ParamStoreKeyCommunityTax, sdk.Dec{},
Expand All @@ -14,7 +14,7 @@ func ParamTypeTable() params.TypeTable {
)
}

// Returns the current CommunityTax rate from the global param store
// returns the current CommunityTax rate from the global param store
// nolint: errcheck
func (k Keeper) GetCommunityTax(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
Expand All @@ -27,7 +27,7 @@ func (k Keeper) SetCommunityTax(ctx sdk.Context, percent sdk.Dec) {
k.paramSpace.Set(ctx, ParamStoreKeyCommunityTax, &percent)
}

// Returns the current BaseProposerReward rate from the global param store
// returns the current BaseProposerReward rate from the global param store
// nolint: errcheck
func (k Keeper) GetBaseProposerReward(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
Expand All @@ -40,7 +40,7 @@ func (k Keeper) SetBaseProposerReward(ctx sdk.Context, percent sdk.Dec) {
k.paramSpace.Set(ctx, ParamStoreKeyBaseProposerReward, &percent)
}

// Returns the current BaseProposerReward rate from the global param store
// returns the current BaseProposerReward rate from the global param store
// nolint: errcheck
func (k Keeper) GetBonusProposerReward(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
Expand Down
Loading

0 comments on commit d5404bf

Please sign in to comment.