Merge PR #2599 from cosmos/jae/dist_refactor

* Rename Pool -> DelRewards; PoolCommission -> ValCommision
* FeePool.Pool -> FeePool.ValPool
* WithdrawalHeight->DelPoolWithdrawalHeight
* OnValidatorBeginUnbonding
* Caught the bug's tail
* Update vi.FeePoolWithdrawalHeight upon bonding
* Fix staking slashUnbondingDelegation bug; fixes simulator failure #9
This commit is contained in:
Jae Kwon 2018-10-26 04:27:55 -07:00 committed by Christopher Goes
parent 3ccc06abb9
commit 0f1fb179c4
16 changed files with 172 additions and 141 deletions

View File

@ -169,7 +169,7 @@ test_sim_gaia_nondeterminism:
test_sim_gaia_fast:
@echo "Running quick Gaia simulation. This may take several minutes..."
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h
test_sim_gaia_multi_seed:
@echo "Running multi-seed Gaia simulation. This may take awhile!"

View File

@ -332,22 +332,26 @@ func NewHooks(dh distr.Hooks, sh slashing.Hooks) Hooks {
var _ sdk.StakingHooks = Hooks{}
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, addr)
func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, valAddr)
}
func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, addr)
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, addr)
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, valAddr)
}
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) {
h.dh.OnValidatorBonded(ctx, addr, operator)
h.sh.OnValidatorBonded(ctx, addr, operator)
func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBonded(ctx, consAddr, valAddr)
h.sh.OnValidatorBonded(ctx, consAddr, valAddr)
}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) {
h.dh.OnValidatorBeginUnbonding(ctx, addr, operator)
h.sh.OnValidatorBeginUnbonding(ctx, addr, operator)
func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorPowerDidChange(ctx, consAddr, valAddr)
h.sh.OnValidatorPowerDidChange(ctx, consAddr, valAddr)
}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBeginUnbonding(ctx, consAddr, valAddr)
h.sh.OnValidatorBeginUnbonding(ctx, consAddr, valAddr)
}
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationCreated(ctx, delAddr, valAddr)

View File

@ -111,12 +111,13 @@ type DelegationSet interface {
// event hooks for staking validator object
type StakingHooks interface {
OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created
OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes
OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted
OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created
OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes
OnValidatorRemoved(ctx Context, valAddr ValAddress) // Must be called when a validator is deleted
OnValidatorBonded(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator begins unbonding
OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding
OnValidatorPowerDidChange(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Called at EndBlock when a validator's power did change
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

View File

@ -27,8 +27,8 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, percentVotes sdk.Dec, proposer s
// apply commission
commission := proposerReward.MulDec(proposerValidator.GetCommission())
remaining := proposerReward.Minus(commission)
proposerDist.PoolCommission = proposerDist.PoolCommission.Plus(commission)
proposerDist.Pool = proposerDist.Pool.Plus(remaining)
proposerDist.ValCommission = proposerDist.ValCommission.Plus(commission)
proposerDist.DelPool = proposerDist.DelPool.Plus(remaining)
// allocate community funding
communityTax := k.GetCommunityTax(ctx)
@ -38,7 +38,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, percentVotes sdk.Dec, proposer s
// set the global pool within the distribution module
poolReceived := feesCollectedDec.Minus(proposerReward).Minus(communityFunding)
feePool.Pool = feePool.Pool.Plus(poolReceived)
feePool.ValPool = feePool.ValPool.Plus(poolReceived)
k.SetValidatorDistInfo(ctx, proposerDist)
k.SetFeePool(ctx, feePool)

View File

@ -35,7 +35,7 @@ func TestAllocateTokensBasic(t *testing.T) {
// initial fee pool should be empty
feePool := keeper.GetFeePool(ctx)
require.Nil(t, feePool.Pool)
require.Nil(t, feePool.ValPool)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
@ -48,8 +48,8 @@ func TestAllocateTokensBasic(t *testing.T) {
percentRemaining := sdk.OneDec().Sub(percentProposer)
feePool = keeper.GetFeePool(ctx)
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
require.Equal(t, 1, len(feePool.ValPool))
require.True(sdk.DecEq(t, expRes, feePool.ValPool[0].Amount))
}
func TestAllocateTokensWithCommunityTax(t *testing.T) {
@ -76,8 +76,8 @@ func TestAllocateTokensWithCommunityTax(t *testing.T) {
percentProposer := sdk.NewDecWithPrec(5, 2)
percentRemaining := sdk.OneDec().Sub(communityTax.Add(percentProposer))
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
require.Equal(t, 1, len(feePool.ValPool))
require.True(sdk.DecEq(t, expRes, feePool.ValPool[0].Amount))
}
func TestAllocateTokensWithPartialPrecommitPower(t *testing.T) {
@ -105,6 +105,6 @@ func TestAllocateTokensWithPartialPrecommitPower(t *testing.T) {
percentProposer := sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(4, 2).Mul(percentPrecommitVotes))
percentRemaining := sdk.OneDec().Sub(communityTax.Add(percentProposer))
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
require.Equal(t, 1, len(feePool.ValPool))
require.True(sdk.DecEq(t, expRes, feePool.ValPool[0].Amount))
}

View File

@ -6,32 +6,40 @@ import (
)
// Create a new validator distribution record
func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
func (k Keeper) onValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
height := ctx.BlockHeight()
vdi := types.ValidatorDistInfo{
OperatorAddr: addr,
OperatorAddr: valAddr,
FeePoolWithdrawalHeight: height,
Pool: types.DecCoins{},
PoolCommission: types.DecCoins{},
DelAccum: types.NewTotalAccum(height),
DelPool: types.DecCoins{},
ValCommission: types.DecCoins{},
}
k.SetValidatorDistInfo(ctx, vdi)
}
// Withdrawal all validator rewards
func (k Keeper) onValidatorModified(ctx sdk.Context, addr sdk.ValAddress) {
// Withdraw all validator rewards
func (k Keeper) onValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
// This doesn't need to be run at genesis
if ctx.BlockHeight() > 0 {
if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil {
if err := k.WithdrawValidatorRewardsAll(ctx, valAddr); err != nil {
panic(err)
}
}
}
// XXX Consider removing this after debugging.
func (k Keeper) onValidatorPowerDidChange(ctx sdk.Context, valAddr sdk.ValAddress) {
vi := k.GetValidatorDistInfo(ctx, valAddr)
if vi.FeePoolWithdrawalHeight != ctx.BlockHeight() {
panic("expected validator dist info FeePoolWithdrawalHeight to be updated, but was not.")
}
}
// Withdrawal all validator distribution rewards and cleanup the distribution record
func (k Keeper) onValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
k.RemoveValidatorDistInfo(ctx, addr)
func (k Keeper) onValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
k.RemoveValidatorDistInfo(ctx, valAddr)
}
//_________________________________________________________________________________________
@ -41,9 +49,9 @@ func (k Keeper) onDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress,
valAddr sdk.ValAddress) {
ddi := types.DelegationDistInfo{
DelegatorAddr: delAddr,
ValOperatorAddr: valAddr,
WithdrawalHeight: ctx.BlockHeight(),
DelegatorAddr: delAddr,
ValOperatorAddr: valAddr,
DelPoolWithdrawalHeight: ctx.BlockHeight(),
}
k.SetDelegationDistInfo(ctx, ddi)
}
@ -77,14 +85,14 @@ var _ sdk.StakingHooks = Hooks{}
func (k Keeper) Hooks() Hooks { return Hooks{k} }
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorCreated(ctx, addr)
func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorCreated(ctx, valAddr)
}
func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr)
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorRemoved(ctx, addr)
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorRemoved(ctx, valAddr)
}
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, valAddr)
@ -97,9 +105,12 @@ func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddres
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onDelegationRemoved(ctx, delAddr, valAddr)
}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr)
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr)
func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorPowerDidChange(ctx, valAddr)
}

View File

@ -6,18 +6,18 @@ import (
// distribution info for a delegation - used to determine entitled rewards
type DelegationDistInfo struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"`
WithdrawalHeight int64 `json:"withdrawal_height"` // last time this delegation withdrew rewards
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"`
DelPoolWithdrawalHeight int64 `json:"del_pool_withdrawal_height"` // last time this delegation withdrew rewards
}
func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress,
currentHeight int64) DelegationDistInfo {
return DelegationDistInfo{
DelegatorAddr: delegatorAddr,
ValOperatorAddr: valOperatorAddr,
WithdrawalHeight: currentHeight,
DelegatorAddr: delegatorAddr,
ValOperatorAddr: valOperatorAddr,
DelPoolWithdrawalHeight: currentHeight,
}
}
@ -40,13 +40,13 @@ func (di DelegationDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
blocks := height - di.WithdrawalHeight
di.WithdrawalHeight = height
blocks := height - di.DelPoolWithdrawalHeight
di.DelPoolWithdrawalHeight = height
accum := delegatorShares.MulInt(sdk.NewInt(blocks))
withdrawalTokens := vi.Pool.MulDec(accum).QuoDec(vi.DelAccum.Accum)
remainingTokens := vi.Pool.Minus(withdrawalTokens)
withdrawalTokens := vi.DelPool.MulDec(accum).QuoDec(vi.DelAccum.Accum)
remDelPool := vi.DelPool.Minus(withdrawalTokens)
vi.Pool = remainingTokens
vi.DelPool = remDelPool
vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum)
return di, vi, fp, withdrawalTokens

View File

@ -26,31 +26,31 @@ func TestWithdrawRewards(t *testing.T) {
// simulate adding some stake for inflation
height = 10
fp.Pool = DecCoins{NewDecCoin("stake", 1000)}
fp.ValPool = DecCoins{NewDecCoin("stake", 1000)}
// withdraw rewards
di1, vi, fp, rewardRecv1 := di1.WithdrawRewards(fp, vi, height, totalBondedTokens,
validatorTokens, validatorDelShares, di1Shares, commissionRate)
assert.Equal(t, height, di1.WithdrawalHeight)
assert.Equal(t, height, di1.DelPoolWithdrawalHeight)
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.ValCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), rewardRecv1[0].Amount))
// add more blocks and inflation
height = 20
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
fp.ValPool[0].Amount = fp.ValPool[0].Amount.Add(sdk.NewDec(1000))
// withdraw rewards
di2, vi, fp, rewardRecv2 := di2.WithdrawRewards(fp, vi, height, totalBondedTokens,
validatorTokens, validatorDelShares, di2Shares, commissionRate)
assert.Equal(t, height, di2.WithdrawalHeight)
assert.Equal(t, height, di2.DelPoolWithdrawalHeight)
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.PoolCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.ValCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(98), rewardRecv2[0].Amount))
}

View File

@ -34,7 +34,7 @@ func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.D
// global fee pool for distribution
type FeePool struct {
TotalValAccum TotalAccum `json:"val_accum"` // total valdator accum held by validators
Pool DecCoins `json:"pool"` // funds for all validators which have yet to be withdrawn
ValPool DecCoins `json:"val_pool"` // funds for all validators which have yet to be withdrawn
CommunityPool DecCoins `json:"community_pool"` // pool for community funds yet to be spent
}
@ -49,7 +49,7 @@ func (f FeePool) UpdateTotalValAccum(height int64, totalBondedTokens sdk.Dec) Fe
func InitialFeePool() FeePool {
return FeePool{
TotalValAccum: NewTotalAccum(0),
Pool: DecCoins{},
ValPool: DecCoins{},
CommunityPool: DecCoins{},
}
}

View File

@ -8,20 +8,20 @@ import (
type ValidatorDistInfo struct {
OperatorAddr sdk.ValAddress `json:"operator_addr"`
FeePoolWithdrawalHeight int64 `json:"global_withdrawal_height"` // last height this validator withdrew from the global pool
Pool DecCoins `json:"pool"` // rewards owed to delegators, commission has already been charged (includes proposer reward)
PoolCommission DecCoins `json:"pool_commission"` // commission collected by this validator (pending withdrawal)
FeePoolWithdrawalHeight int64 `json:"fee_pool_withdrawal_height"` // last height this validator withdrew from the global pool
DelAccum TotalAccum `json:"del_accum"` // total proposer pool accumulation factor held by delegators
DelAccum TotalAccum `json:"del_accum"` // total accumulation factor held by delegators
DelPool DecCoins `json:"del_pool"` // rewards owed to delegators, commission has already been charged (includes proposer reward)
ValCommission DecCoins `json:"val_commission"` // commission collected by this validator (pending withdrawal)
}
func NewValidatorDistInfo(operatorAddr sdk.ValAddress, currentHeight int64) ValidatorDistInfo {
return ValidatorDistInfo{
OperatorAddr: operatorAddr,
FeePoolWithdrawalHeight: currentHeight,
Pool: DecCoins{},
PoolCommission: DecCoins{},
DelPool: DecCoins{},
DelAccum: NewTotalAccum(currentHeight),
ValCommission: DecCoins{},
}
}
@ -46,6 +46,7 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
fp = fp.UpdateTotalValAccum(height, totalBonded)
if fp.TotalValAccum.Accum.IsZero() {
vi.FeePoolWithdrawalHeight = height
return vi, fp
}
@ -57,16 +58,16 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
if accum.GT(fp.TotalValAccum.Accum) {
panic("individual accum should never be greater than the total")
}
withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum)
remainingTokens := fp.Pool.Minus(withdrawalTokens)
withdrawalTokens := fp.ValPool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum)
remValPool := fp.ValPool.Minus(withdrawalTokens)
commission := withdrawalTokens.MulDec(commissionRate)
afterCommission := withdrawalTokens.Minus(commission)
fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum)
fp.Pool = remainingTokens
vi.PoolCommission = vi.PoolCommission.Plus(commission)
vi.Pool = vi.Pool.Plus(afterCommission)
fp.ValPool = remValPool
vi.ValCommission = vi.ValCommission.Plus(commission)
vi.DelPool = vi.DelPool.Plus(afterCommission)
return vi, fp
}
@ -77,8 +78,8 @@ func (vi ValidatorDistInfo) WithdrawCommission(fp FeePool, height int64,
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
withdrawalTokens := vi.PoolCommission
vi.PoolCommission = DecCoins{} // zero
withdrawalTokens := vi.ValCommission
vi.ValCommission = DecCoins{} // zero
return vi, fp, withdrawalTokens
}

View File

@ -26,29 +26,29 @@ func TestTakeFeePoolRewards(t *testing.T) {
// simulate adding some stake for inflation
height = 10
fp.Pool = DecCoins{NewDecCoin("stake", 1000)}
fp.ValPool = DecCoins{NewDecCoin("stake", 1000)}
vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.ValCommission[0].Amount))
vi2, fp = vi2.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens2, commissionRate2)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.Pool[0].Amount))
assert.True(sdk.DecEq(t, vi2.PoolCommission[0].Amount, sdk.NewDec(12)))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.DelPool[0].Amount))
assert.True(sdk.DecEq(t, vi2.ValCommission[0].Amount, sdk.NewDec(12)))
// add more blocks and inflation
height = 20
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
fp.ValPool[0].Amount = fp.ValPool[0].Amount.Add(sdk.NewDec(1000))
vi3, fp = vi3.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens3, commissionRate3)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.Pool[0].Amount))
assert.True(sdk.DecEq(t, vi3.PoolCommission[0].Amount, sdk.NewDec(40)))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.DelPool[0].Amount))
assert.True(sdk.DecEq(t, vi3.ValCommission[0].Amount, sdk.NewDec(40)))
}
func TestWithdrawCommission(t *testing.T) {
@ -63,23 +63,23 @@ func TestWithdrawCommission(t *testing.T) {
// simulate adding some stake for inflation
height = 10
fp.Pool = DecCoins{NewDecCoin("stake", 1000)}
fp.ValPool = DecCoins{NewDecCoin("stake", 1000)}
// for a more fun staring condition, have an non-withdraw update
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.ValCommission[0].Amount))
// add more blocks and inflation
height = 20
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
fp.ValPool[0].Amount = fp.ValPool[0].Amount.Add(sdk.NewDec(1000))
vi, fp, commissionRecv := vi.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens, commissionRate)
require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.Pool[0].Amount))
assert.Zero(t, len(vi.PoolCommission))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.DelPool[0].Amount))
assert.Zero(t, len(vi.ValCommission))
assert.True(sdk.DecEq(t, sdk.NewDec(4), commissionRecv[0].Amount))
}

View File

@ -51,16 +51,18 @@ func (k Keeper) Hooks() Hooks {
}
// Implements sdk.ValidatorHooks
func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
h.k.onValidatorBonded(ctx, address, operator)
func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorBonded(ctx, consAddr, valAddr)
}
// Implements sdk.ValidatorHooks
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
h.k.onValidatorBeginUnbonding(ctx, address, operator)
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorBeginUnbonding(ctx, consAddr, valAddr)
}
// nolint - unused hooks
func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
}
func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {}

View File

@ -6,32 +6,38 @@ import (
)
// Expose the hooks if present
func (k Keeper) OnValidatorCreated(ctx sdk.Context, address sdk.ValAddress) {
func (k Keeper) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorCreated(ctx, address)
k.hooks.OnValidatorCreated(ctx, valAddr)
}
}
func (k Keeper) OnValidatorModified(ctx sdk.Context, address sdk.ValAddress) {
func (k Keeper) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorModified(ctx, address)
k.hooks.OnValidatorModified(ctx, valAddr)
}
}
func (k Keeper) OnValidatorRemoved(ctx sdk.Context, address sdk.ValAddress) {
func (k Keeper) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorRemoved(ctx, address)
k.hooks.OnValidatorRemoved(ctx, valAddr)
}
}
func (k Keeper) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
func (k Keeper) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorBonded(ctx, address, operator)
k.hooks.OnValidatorBonded(ctx, consAddr, valAddr)
}
}
func (k Keeper) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
func (k Keeper) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorBeginUnbonding(ctx, address, operator)
k.hooks.OnValidatorPowerDidChange(ctx, consAddr, valAddr)
}
}
func (k Keeper) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.OnValidatorBeginUnbonding(ctx, consAddr, valAddr)
}
}

View File

@ -181,7 +181,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
// Burn loose tokens
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
pool.LooseTokens = pool.LooseTokens.Sub(slashAmount)
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(unbondingSlashAmount))
k.SetPool(ctx, pool)
}

View File

@ -40,8 +40,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
for ; iterator.Valid() && count < int(maxValidators); iterator.Next() {
// fetch the validator
operator := sdk.ValAddress(iterator.Value())
validator := k.mustGetValidator(ctx, operator)
valAddr := sdk.ValAddress(iterator.Value())
validator := k.mustGetValidator(ctx, valAddr)
if validator.Jailed {
panic("should never retrieve a jailed validator from the power store")
@ -67,9 +67,9 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
}
// fetch the old power bytes
var operatorBytes [sdk.AddrLen]byte
copy(operatorBytes[:], operator[:])
oldPowerBytes, found := last[operatorBytes]
var valAddrBytes [sdk.AddrLen]byte
copy(valAddrBytes[:], valAddr[:])
oldPowerBytes, found := last[valAddrBytes]
// calculate the new power bytes
newPower := validator.BondedTokens().RoundInt64()
@ -78,12 +78,18 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
updates = append(updates, validator.ABCIValidatorUpdate())
// XXX Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight.
// XXX This hook probably shouldn't exist. Maybe rethink the hook system.
if k.hooks != nil {
k.hooks.OnValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr)
}
// set validator power on lookup index.
k.SetLastValidatorPower(ctx, operator, sdk.NewInt(newPower))
k.SetLastValidatorPower(ctx, valAddr, sdk.NewInt(newPower))
}
// validator still in the validator set, so delete from the copy
delete(last, operatorBytes)
delete(last, valAddrBytes)
// keep count
count++
@ -94,10 +100,10 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
noLongerBonded := k.sortNoLongerBonded(last)
// iterate through the sorted no-longer-bonded validators
for _, operator := range noLongerBonded {
for _, valAddrBytes := range noLongerBonded {
// fetch the validator
validator := k.mustGetValidator(ctx, sdk.ValAddress(operator))
validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes))
// bonded to unbonding
k.bondedToUnbonding(ctx, validator)
@ -108,7 +114,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
}
// delete from the bonded validator index
k.DeleteLastValidatorPower(ctx, sdk.ValAddress(operator))
k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes))
// update the validator set
updates = append(updates, validator.ABCIValidatorUpdateZero())
@ -257,11 +263,11 @@ func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
for ; iterator.Valid(); iterator.Next() {
var operator [sdk.AddrLen]byte
copy(operator[:], iterator.Key()[1:])
var valAddr [sdk.AddrLen]byte
copy(valAddr[:], iterator.Key()[1:])
powerBytes := iterator.Value()
last[operator] = make([]byte, len(powerBytes))
copy(last[operator][:], powerBytes[:])
last[valAddr] = make([]byte, len(powerBytes))
copy(last[valAddr][:], powerBytes[:])
}
return last
}
@ -272,10 +278,10 @@ func (k Keeper) sortNoLongerBonded(last validatorsByAddr) [][]byte {
// sort the map keys for determinism
noLongerBonded := make([][]byte, len(last))
index := 0
for operatorBytes := range last {
operator := make([]byte, sdk.AddrLen)
copy(operator[:], operatorBytes[:])
noLongerBonded[index] = operator
for valAddrBytes := range last {
valAddr := make([]byte, sdk.AddrLen)
copy(valAddr[:], valAddrBytes[:])
noLongerBonded[index] = valAddr
index++
}
// sorted by address - order doesn't matter

View File

@ -68,12 +68,12 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper
loose = loose.Add(feePool.CommunityPool.AmountOf("steak"))
// add validator distribution pool
loose = loose.Add(feePool.Pool.AmountOf("steak"))
loose = loose.Add(feePool.ValPool.AmountOf("steak"))
// add validator distribution commission and yet-to-be-withdrawn-by-delegators
d.IterateValidatorDistInfos(ctx, func(_ int64, distInfo distribution.ValidatorDistInfo) (stop bool) {
loose = loose.Add(distInfo.Pool.AmountOf("steak"))
loose = loose.Add(distInfo.PoolCommission.AmountOf("steak"))
loose = loose.Add(distInfo.ValCommission.AmountOf("steak"))
loose = loose.Add(distInfo.DelPool.AmountOf("steak"))
return false
})