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: test_sim_gaia_fast:
@echo "Running quick Gaia simulation. This may take several minutes..." @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: test_sim_gaia_multi_seed:
@echo "Running multi-seed Gaia simulation. This may take awhile!" @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{} var _ sdk.StakingHooks = Hooks{}
// nolint // nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, addr) h.dh.OnValidatorCreated(ctx, valAddr)
} }
func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, addr) h.dh.OnValidatorModified(ctx, valAddr)
} }
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, addr) h.dh.OnValidatorRemoved(ctx, valAddr)
} }
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) { func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBonded(ctx, addr, operator) h.dh.OnValidatorBonded(ctx, consAddr, valAddr)
h.sh.OnValidatorBonded(ctx, addr, operator) h.sh.OnValidatorBonded(ctx, consAddr, valAddr)
} }
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) { func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBeginUnbonding(ctx, addr, operator) h.dh.OnValidatorPowerDidChange(ctx, consAddr, valAddr)
h.sh.OnValidatorBeginUnbonding(ctx, addr, operator) 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) { func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationCreated(ctx, delAddr, valAddr) h.dh.OnDelegationCreated(ctx, delAddr, valAddr)

View File

@ -111,12 +111,13 @@ type DelegationSet interface {
// event hooks for staking validator object // event hooks for staking validator object
type StakingHooks interface { type StakingHooks interface {
OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created
OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes
OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted 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 OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator begins unbonding 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 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 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 // apply commission
commission := proposerReward.MulDec(proposerValidator.GetCommission()) commission := proposerReward.MulDec(proposerValidator.GetCommission())
remaining := proposerReward.Minus(commission) remaining := proposerReward.Minus(commission)
proposerDist.PoolCommission = proposerDist.PoolCommission.Plus(commission) proposerDist.ValCommission = proposerDist.ValCommission.Plus(commission)
proposerDist.Pool = proposerDist.Pool.Plus(remaining) proposerDist.DelPool = proposerDist.DelPool.Plus(remaining)
// allocate community funding // allocate community funding
communityTax := k.GetCommunityTax(ctx) 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 // set the global pool within the distribution module
poolReceived := feesCollectedDec.Minus(proposerReward).Minus(communityFunding) poolReceived := feesCollectedDec.Minus(proposerReward).Minus(communityFunding)
feePool.Pool = feePool.Pool.Plus(poolReceived) feePool.ValPool = feePool.ValPool.Plus(poolReceived)
k.SetValidatorDistInfo(ctx, proposerDist) k.SetValidatorDistInfo(ctx, proposerDist)
k.SetFeePool(ctx, feePool) k.SetFeePool(ctx, feePool)

View File

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

View File

@ -6,32 +6,40 @@ import (
) )
// Create a new validator distribution record // 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() height := ctx.BlockHeight()
vdi := types.ValidatorDistInfo{ vdi := types.ValidatorDistInfo{
OperatorAddr: addr, OperatorAddr: valAddr,
FeePoolWithdrawalHeight: height, FeePoolWithdrawalHeight: height,
Pool: types.DecCoins{},
PoolCommission: types.DecCoins{},
DelAccum: types.NewTotalAccum(height), DelAccum: types.NewTotalAccum(height),
DelPool: types.DecCoins{},
ValCommission: types.DecCoins{},
} }
k.SetValidatorDistInfo(ctx, vdi) k.SetValidatorDistInfo(ctx, vdi)
} }
// Withdrawal all validator rewards // Withdraw all validator rewards
func (k Keeper) onValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { func (k Keeper) onValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
// This doesn't need to be run at genesis // This doesn't need to be run at genesis
if ctx.BlockHeight() > 0 { if ctx.BlockHeight() > 0 {
if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil { if err := k.WithdrawValidatorRewardsAll(ctx, valAddr); err != nil {
panic(err) 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 // Withdrawal all validator distribution rewards and cleanup the distribution record
func (k Keeper) onValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { func (k Keeper) onValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
k.RemoveValidatorDistInfo(ctx, addr) k.RemoveValidatorDistInfo(ctx, valAddr)
} }
//_________________________________________________________________________________________ //_________________________________________________________________________________________
@ -41,9 +49,9 @@ func (k Keeper) onDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress,
valAddr sdk.ValAddress) { valAddr sdk.ValAddress) {
ddi := types.DelegationDistInfo{ ddi := types.DelegationDistInfo{
DelegatorAddr: delAddr, DelegatorAddr: delAddr,
ValOperatorAddr: valAddr, ValOperatorAddr: valAddr,
WithdrawalHeight: ctx.BlockHeight(), DelPoolWithdrawalHeight: ctx.BlockHeight(),
} }
k.SetDelegationDistInfo(ctx, ddi) k.SetDelegationDistInfo(ctx, ddi)
} }
@ -77,14 +85,14 @@ var _ sdk.StakingHooks = Hooks{}
func (k Keeper) Hooks() Hooks { return Hooks{k} } func (k Keeper) Hooks() Hooks { return Hooks{k} }
// nolint // nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorCreated(ctx, addr) h.k.onValidatorCreated(ctx, valAddr)
} }
func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr) h.k.onValidatorModified(ctx, valAddr)
} }
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.k.onValidatorRemoved(ctx, addr) h.k.onValidatorRemoved(ctx, valAddr)
} }
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, valAddr) 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) { func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onDelegationRemoved(ctx, delAddr, valAddr) h.k.onDelegationRemoved(ctx, delAddr, valAddr)
} }
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) { func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr) h.k.onValidatorModified(ctx, valAddr)
} }
func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) { func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorModified(ctx, addr) 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 // distribution info for a delegation - used to determine entitled rewards
type DelegationDistInfo struct { type DelegationDistInfo struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"` ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"`
WithdrawalHeight int64 `json:"withdrawal_height"` // last time this delegation withdrew rewards DelPoolWithdrawalHeight int64 `json:"del_pool_withdrawal_height"` // last time this delegation withdrew rewards
} }
func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress, func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress,
currentHeight int64) DelegationDistInfo { currentHeight int64) DelegationDistInfo {
return DelegationDistInfo{ return DelegationDistInfo{
DelegatorAddr: delegatorAddr, DelegatorAddr: delegatorAddr,
ValOperatorAddr: valOperatorAddr, ValOperatorAddr: valOperatorAddr,
WithdrawalHeight: currentHeight, DelPoolWithdrawalHeight: currentHeight,
} }
} }
@ -40,13 +40,13 @@ func (di DelegationDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate) vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
blocks := height - di.WithdrawalHeight blocks := height - di.DelPoolWithdrawalHeight
di.WithdrawalHeight = height di.DelPoolWithdrawalHeight = height
accum := delegatorShares.MulInt(sdk.NewInt(blocks)) accum := delegatorShares.MulInt(sdk.NewInt(blocks))
withdrawalTokens := vi.Pool.MulDec(accum).QuoDec(vi.DelAccum.Accum) withdrawalTokens := vi.DelPool.MulDec(accum).QuoDec(vi.DelAccum.Accum)
remainingTokens := vi.Pool.Minus(withdrawalTokens) remDelPool := vi.DelPool.Minus(withdrawalTokens)
vi.Pool = remainingTokens vi.DelPool = remDelPool
vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum) vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum)
return di, vi, fp, withdrawalTokens return di, vi, fp, withdrawalTokens

View File

@ -26,31 +26,31 @@ func TestWithdrawRewards(t *testing.T) {
// simulate adding some stake for inflation // simulate adding some stake for inflation
height = 10 height = 10
fp.Pool = DecCoins{NewDecCoin("stake", 1000)} fp.ValPool = DecCoins{NewDecCoin("stake", 1000)}
// withdraw rewards // withdraw rewards
di1, vi, fp, rewardRecv1 := di1.WithdrawRewards(fp, vi, height, totalBondedTokens, di1, vi, fp, rewardRecv1 := di1.WithdrawRewards(fp, vi, height, totalBondedTokens,
validatorTokens, validatorDelShares, di1Shares, commissionRate) 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.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[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)) assert.True(sdk.DecEq(t, sdk.NewDec(49), rewardRecv1[0].Amount))
// add more blocks and inflation // add more blocks and inflation
height = 20 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 // withdraw rewards
di2, vi, fp, rewardRecv2 := di2.WithdrawRewards(fp, vi, height, totalBondedTokens, di2, vi, fp, rewardRecv2 := di2.WithdrawRewards(fp, vi, height, totalBondedTokens,
validatorTokens, validatorDelShares, di2Shares, commissionRate) 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.TotalValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.PoolCommission[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)) 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 // global fee pool for distribution
type FeePool struct { type FeePool struct {
TotalValAccum TotalAccum `json:"val_accum"` // total valdator accum held by validators 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 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 { func InitialFeePool() FeePool {
return FeePool{ return FeePool{
TotalValAccum: NewTotalAccum(0), TotalValAccum: NewTotalAccum(0),
Pool: DecCoins{}, ValPool: DecCoins{},
CommunityPool: DecCoins{}, CommunityPool: DecCoins{},
} }
} }

View File

@ -8,20 +8,20 @@ import (
type ValidatorDistInfo struct { type ValidatorDistInfo struct {
OperatorAddr sdk.ValAddress `json:"operator_addr"` OperatorAddr sdk.ValAddress `json:"operator_addr"`
FeePoolWithdrawalHeight int64 `json:"global_withdrawal_height"` // last height this validator withdrew from the global pool FeePoolWithdrawalHeight int64 `json:"fee_pool_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)
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 { func NewValidatorDistInfo(operatorAddr sdk.ValAddress, currentHeight int64) ValidatorDistInfo {
return ValidatorDistInfo{ return ValidatorDistInfo{
OperatorAddr: operatorAddr, OperatorAddr: operatorAddr,
FeePoolWithdrawalHeight: currentHeight, FeePoolWithdrawalHeight: currentHeight,
Pool: DecCoins{}, DelPool: DecCoins{},
PoolCommission: DecCoins{},
DelAccum: NewTotalAccum(currentHeight), DelAccum: NewTotalAccum(currentHeight),
ValCommission: DecCoins{},
} }
} }
@ -46,6 +46,7 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
fp = fp.UpdateTotalValAccum(height, totalBonded) fp = fp.UpdateTotalValAccum(height, totalBonded)
if fp.TotalValAccum.Accum.IsZero() { if fp.TotalValAccum.Accum.IsZero() {
vi.FeePoolWithdrawalHeight = height
return vi, fp return vi, fp
} }
@ -57,16 +58,16 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
if accum.GT(fp.TotalValAccum.Accum) { if accum.GT(fp.TotalValAccum.Accum) {
panic("individual accum should never be greater than the total") panic("individual accum should never be greater than the total")
} }
withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum) withdrawalTokens := fp.ValPool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum)
remainingTokens := fp.Pool.Minus(withdrawalTokens) remValPool := fp.ValPool.Minus(withdrawalTokens)
commission := withdrawalTokens.MulDec(commissionRate) commission := withdrawalTokens.MulDec(commissionRate)
afterCommission := withdrawalTokens.Minus(commission) afterCommission := withdrawalTokens.Minus(commission)
fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum) fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum)
fp.Pool = remainingTokens fp.ValPool = remValPool
vi.PoolCommission = vi.PoolCommission.Plus(commission) vi.ValCommission = vi.ValCommission.Plus(commission)
vi.Pool = vi.Pool.Plus(afterCommission) vi.DelPool = vi.DelPool.Plus(afterCommission)
return vi, fp return vi, fp
} }
@ -77,8 +78,8 @@ func (vi ValidatorDistInfo) WithdrawCommission(fp FeePool, height int64,
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate) vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
withdrawalTokens := vi.PoolCommission withdrawalTokens := vi.ValCommission
vi.PoolCommission = DecCoins{} // zero vi.ValCommission = DecCoins{} // zero
return vi, fp, withdrawalTokens return vi, fp, withdrawalTokens
} }

View File

@ -26,29 +26,29 @@ func TestTakeFeePoolRewards(t *testing.T) {
// simulate adding some stake for inflation // simulate adding some stake for inflation
height = 10 height = 10
fp.Pool = DecCoins{NewDecCoin("stake", 1000)} fp.ValPool = DecCoins{NewDecCoin("stake", 1000)}
vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1) vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum)) 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(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.ValCommission[0].Amount))
vi2, fp = vi2.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens2, commissionRate2) vi2, fp = vi2.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens2, commissionRate2)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum)) 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(500), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.DelPool[0].Amount))
assert.True(sdk.DecEq(t, vi2.PoolCommission[0].Amount, sdk.NewDec(12))) assert.True(sdk.DecEq(t, vi2.ValCommission[0].Amount, sdk.NewDec(12)))
// add more blocks and inflation // add more blocks and inflation
height = 20 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) vi3, fp = vi3.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens3, commissionRate3)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum)) 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(500), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.DelPool[0].Amount))
assert.True(sdk.DecEq(t, vi3.PoolCommission[0].Amount, sdk.NewDec(40))) assert.True(sdk.DecEq(t, vi3.ValCommission[0].Amount, sdk.NewDec(40)))
} }
func TestWithdrawCommission(t *testing.T) { func TestWithdrawCommission(t *testing.T) {
@ -63,23 +63,23 @@ func TestWithdrawCommission(t *testing.T) {
// simulate adding some stake for inflation // simulate adding some stake for inflation
height = 10 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 // for a more fun staring condition, have an non-withdraw update
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate) vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum)) 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(900), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.DelPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.ValCommission[0].Amount))
// add more blocks and inflation // add more blocks and inflation
height = 20 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) vi, fp, commissionRecv := vi.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens, commissionRate)
require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.TotalValAccum.Accum)) 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(1800), fp.ValPool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.DelPool[0].Amount))
assert.Zero(t, len(vi.PoolCommission)) assert.Zero(t, len(vi.ValCommission))
assert.True(sdk.DecEq(t, sdk.NewDec(4), commissionRecv[0].Amount)) 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 // Implements sdk.ValidatorHooks
func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) { func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorBonded(ctx, address, operator) h.k.onValidatorBonded(ctx, consAddr, valAddr)
} }
// Implements sdk.ValidatorHooks // Implements sdk.ValidatorHooks
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) { func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.k.onValidatorBeginUnbonding(ctx, address, operator) h.k.onValidatorBeginUnbonding(ctx, consAddr, valAddr)
} }
// nolint - unused hooks // 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) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {}

View File

@ -6,32 +6,38 @@ import (
) )
// Expose the hooks if present // 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 { 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 { 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 { 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 { 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 { 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 // Burn loose tokens
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760 // 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) 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() { for ; iterator.Valid() && count < int(maxValidators); iterator.Next() {
// fetch the validator // fetch the validator
operator := sdk.ValAddress(iterator.Value()) valAddr := sdk.ValAddress(iterator.Value())
validator := k.mustGetValidator(ctx, operator) validator := k.mustGetValidator(ctx, valAddr)
if validator.Jailed { if validator.Jailed {
panic("should never retrieve a jailed validator from the power store") 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 // fetch the old power bytes
var operatorBytes [sdk.AddrLen]byte var valAddrBytes [sdk.AddrLen]byte
copy(operatorBytes[:], operator[:]) copy(valAddrBytes[:], valAddr[:])
oldPowerBytes, found := last[operatorBytes] oldPowerBytes, found := last[valAddrBytes]
// calculate the new power bytes // calculate the new power bytes
newPower := validator.BondedTokens().RoundInt64() newPower := validator.BondedTokens().RoundInt64()
@ -78,12 +78,18 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
updates = append(updates, validator.ABCIValidatorUpdate()) 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. // 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 // validator still in the validator set, so delete from the copy
delete(last, operatorBytes) delete(last, valAddrBytes)
// keep count // keep count
count++ count++
@ -94,10 +100,10 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
noLongerBonded := k.sortNoLongerBonded(last) noLongerBonded := k.sortNoLongerBonded(last)
// iterate through the sorted no-longer-bonded validators // iterate through the sorted no-longer-bonded validators
for _, operator := range noLongerBonded { for _, valAddrBytes := range noLongerBonded {
// fetch the validator // fetch the validator
validator := k.mustGetValidator(ctx, sdk.ValAddress(operator)) validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes))
// bonded to unbonding // bonded to unbonding
k.bondedToUnbonding(ctx, validator) k.bondedToUnbonding(ctx, validator)
@ -108,7 +114,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
} }
// delete from the bonded validator index // delete from the bonded validator index
k.DeleteLastValidatorPower(ctx, sdk.ValAddress(operator)) k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes))
// update the validator set // update the validator set
updates = append(updates, validator.ABCIValidatorUpdateZero()) updates = append(updates, validator.ABCIValidatorUpdateZero())
@ -257,11 +263,11 @@ func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
for ; iterator.Valid(); iterator.Next() { for ; iterator.Valid(); iterator.Next() {
var operator [sdk.AddrLen]byte var valAddr [sdk.AddrLen]byte
copy(operator[:], iterator.Key()[1:]) copy(valAddr[:], iterator.Key()[1:])
powerBytes := iterator.Value() powerBytes := iterator.Value()
last[operator] = make([]byte, len(powerBytes)) last[valAddr] = make([]byte, len(powerBytes))
copy(last[operator][:], powerBytes[:]) copy(last[valAddr][:], powerBytes[:])
} }
return last return last
} }
@ -272,10 +278,10 @@ func (k Keeper) sortNoLongerBonded(last validatorsByAddr) [][]byte {
// sort the map keys for determinism // sort the map keys for determinism
noLongerBonded := make([][]byte, len(last)) noLongerBonded := make([][]byte, len(last))
index := 0 index := 0
for operatorBytes := range last { for valAddrBytes := range last {
operator := make([]byte, sdk.AddrLen) valAddr := make([]byte, sdk.AddrLen)
copy(operator[:], operatorBytes[:]) copy(valAddr[:], valAddrBytes[:])
noLongerBonded[index] = operator noLongerBonded[index] = valAddr
index++ index++
} }
// sorted by address - order doesn't matter // 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")) loose = loose.Add(feePool.CommunityPool.AmountOf("steak"))
// add validator distribution pool // 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 // add validator distribution commission and yet-to-be-withdrawn-by-delegators
d.IterateValidatorDistInfos(ctx, func(_ int64, distInfo distribution.ValidatorDistInfo) (stop bool) { d.IterateValidatorDistInfos(ctx, func(_ int64, distInfo distribution.ValidatorDistInfo) (stop bool) {
loose = loose.Add(distInfo.Pool.AmountOf("steak")) loose = loose.Add(distInfo.ValCommission.AmountOf("steak"))
loose = loose.Add(distInfo.PoolCommission.AmountOf("steak")) loose = loose.Add(distInfo.DelPool.AmountOf("steak"))
return false return false
}) })