From 48aa9a6ad50c36a3587db92d46043d79fb1347d1 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 4 Sep 2018 16:19:37 -0400 Subject: [PATCH] working, moved WIP distribution spec to attic --- .../WIP-lamborghini-distribution/README.md | 0 .../WIP-lamborghini-distribution/end_block.md | 0 .../example_sheet/distribution.xlsx | Bin .../future_improvements.md | 0 .../WIP-lamborghini-distribution/overview.md | 0 .../WIP-lamborghini-distribution/state.md | 0 .../transactions.md | 0 .../WIP-lamborghini-distribution/triggers.md | 0 docs/spec/distribution/transactions.md | 6 +- x/distribution/handler.go | 136 ++++++++++++++++++ x/distribution/hooks.go | 28 ++++ x/distribution/keeper/keeper.go | 101 +++++++++++++ x/distribution/keeper/keeper_test.go | 2 + x/distribution/types/delegator_info.go | 30 ++++ x/distribution/types/global.go | 30 ++++ x/distribution/types/types.go | 35 ----- x/distribution/types/validator_info.go | 57 ++++++++ 17 files changed, 387 insertions(+), 38 deletions(-) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/README.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/end_block.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/example_sheet/distribution.xlsx (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/future_improvements.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/overview.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/state.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/transactions.md (100%) rename docs/{spec/distribution => _attic}/WIP-lamborghini-distribution/triggers.md (100%) create mode 100644 x/distribution/hooks.go create mode 100644 x/distribution/types/delegator_info.go create mode 100644 x/distribution/types/global.go delete mode 100644 x/distribution/types/types.go create mode 100644 x/distribution/types/validator_info.go diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/README.md b/docs/_attic/WIP-lamborghini-distribution/README.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/README.md rename to docs/_attic/WIP-lamborghini-distribution/README.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/end_block.md b/docs/_attic/WIP-lamborghini-distribution/end_block.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/end_block.md rename to docs/_attic/WIP-lamborghini-distribution/end_block.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/example_sheet/distribution.xlsx b/docs/_attic/WIP-lamborghini-distribution/example_sheet/distribution.xlsx similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/example_sheet/distribution.xlsx rename to docs/_attic/WIP-lamborghini-distribution/example_sheet/distribution.xlsx diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/future_improvements.md b/docs/_attic/WIP-lamborghini-distribution/future_improvements.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/future_improvements.md rename to docs/_attic/WIP-lamborghini-distribution/future_improvements.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/overview.md b/docs/_attic/WIP-lamborghini-distribution/overview.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/overview.md rename to docs/_attic/WIP-lamborghini-distribution/overview.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/state.md b/docs/_attic/WIP-lamborghini-distribution/state.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/state.md rename to docs/_attic/WIP-lamborghini-distribution/state.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/transactions.md b/docs/_attic/WIP-lamborghini-distribution/transactions.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/transactions.md rename to docs/_attic/WIP-lamborghini-distribution/transactions.md diff --git a/docs/spec/distribution/WIP-lamborghini-distribution/triggers.md b/docs/_attic/WIP-lamborghini-distribution/triggers.md similarity index 100% rename from docs/spec/distribution/WIP-lamborghini-distribution/triggers.md rename to docs/_attic/WIP-lamborghini-distribution/triggers.md diff --git a/docs/spec/distribution/transactions.md b/docs/spec/distribution/transactions.md index 0b89ae44e..861620620 100644 --- a/docs/spec/distribution/transactions.md +++ b/docs/spec/distribution/transactions.md @@ -148,7 +148,7 @@ from the passive global pool to their own pool. It is at this point that the commission is withdrawn ``` -func (vi ValidatorDistInfo) TakeAccum(g Global, height int64, totalBonded, vdTokens, commissionRate Dec) ( +func (vi ValidatorDistInfo) TakeGlobalRewards(g Global, height int64, totalBonded, vdTokens, commissionRate Dec) ( vi ValidatorDistInfo, g Global) g.UpdateTotalValAccum(height, totalBondedShares) @@ -180,7 +180,7 @@ func (di DelegatorDistInfo) WithdrawRewards(g Global, vi ValidatorDistInfo, di DelegatorDistInfo, g Global, withdrawn DecCoins) vi.UpdateTotalDelAccum(height, totalDelShares) - g = vi.TakeAccum(g, height, totalBonded, vdTokens, commissionRate) + g = vi.TakeGlobalRewards(g, height, totalBonded, vdTokens, commissionRate) blocks = height - di.WithdrawalHeight di.WithdrawalHeight = height @@ -204,7 +204,7 @@ func (vi ValidatorDistInfo) WithdrawCommission(g Global, height int64, totalBonded, vdTokens, commissionRate Dec) ( vi ValidatorDistInfo, g Global, withdrawn DecCoins) - g = vi.TakeAccum(g, height, totalBonded, vdTokens, commissionRate) + g = vi.TakeGlobalRewards(g, height, totalBonded, vdTokens, commissionRate) withdrawalTokens := vi.PoolCommission vi.PoolCommission = 0 diff --git a/x/distribution/handler.go b/x/distribution/handler.go index da129855e..bd317b9c1 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -1,8 +1,144 @@ package stake import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/keeper" ) // burn burn burn func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} + +// Called every block, process inflation, update validator set +func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) { + pool := k.GetPool(ctx) + + // Process provision inflation + blockTime := ctx.BlockHeader().Time + if blockTime.Sub(pool.InflationLastTime) >= time.Hour { + params := k.GetParams(ctx) + pool.InflationLastTime = blockTime + pool = pool.ProcessProvisions(params) + k.SetPool(ctx, pool) + } + + // reset the intra-transaction counter + k.SetIntraTxCounter(ctx, 0) + + // calculate validator set changes + ValidatorUpdates = k.GetTendermintUpdates(ctx) + k.ClearTendermintUpdates(ctx) + return +} + +/* +func AllocateFees(feesCollected sdk.Coins, global Global, proposer ValidatorDistribution, + sumPowerPrecommitValidators, totalBondedTokens, communityTax, + proposerCommissionRate sdk.Dec) + + feesCollectedDec = MakeDecCoins(feesCollected) + proposerReward = feesCollectedDec * (0.01 + 0.04 + * sumPowerPrecommitValidators / totalBondedTokens) + + commission = proposerReward * proposerCommissionRate + proposer.PoolCommission += commission + proposer.Pool += proposerReward - commission + + communityFunding = feesCollectedDec * communityTax + global.CommunityFund += communityFunding + + poolReceived = feesCollectedDec - proposerReward - communityFunding + global.Pool += poolReceived + global.EverReceivedPool += poolReceived + global.LastReceivedPool = poolReceived + + SetValidatorDistribution(proposer) + SetGlobal(global) + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +type TxWithdrawDelegationRewardsAll struct { + delegatorAddr sdk.AccAddress + withdrawAddr sdk.AccAddress // address to make the withdrawal to +} + +func WithdrawDelegationRewardsAll(delegatorAddr, withdrawAddr sdk.AccAddress) + height = GetHeight() + withdraw = GetDelegatorRewardsAll(delegatorAddr, height) + AddCoins(withdrawAddr, withdraw.TruncateDecimal()) + +func GetDelegatorRewardsAll(delegatorAddr sdk.AccAddress, height int64) DecCoins + + // get all distribution scenarios + delegations = GetDelegations(delegatorAddr) + + // collect all entitled rewards + withdraw = 0 + pool = stake.GetPool() + global = GetGlobal() + for delegation = range delegations + delInfo = GetDelegationDistInfo(delegation.DelegatorAddr, + delegation.ValidatorAddr) + valInfo = GetValidatorDistInfo(delegation.ValidatorAddr) + validator = GetValidator(delegation.ValidatorAddr) + + global, diWithdraw = delInfo.WithdrawRewards(global, valInfo, height, pool.BondedTokens, + validator.Tokens, validator.DelegatorShares, validator.Commission) + withdraw += diWithdraw + + SetGlobal(global) + return withdraw + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +type TxWithdrawDelegationReward struct { + delegatorAddr sdk.AccAddress + validatorAddr sdk.AccAddress + withdrawAddr sdk.AccAddress // address to make the withdrawal to +} + +func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.AccAddress) + height = GetHeight() + + // get all distribution scenarios + pool = stake.GetPool() + global = GetGlobal() + delInfo = GetDelegationDistInfo(delegatorAddr, + validatorAddr) + valInfo = GetValidatorDistInfo(validatorAddr) + validator = GetValidator(validatorAddr) + + global, withdraw = delInfo.WithdrawRewards(global, valInfo, height, pool.BondedTokens, + validator.Tokens, validator.DelegatorShares, validator.Commission) + + SetGlobal(global) + AddCoins(withdrawAddr, withdraw.TruncateDecimal()) + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +type TxWithdrawValidatorRewardsAll struct { + operatorAddr sdk.AccAddress // validator address to withdraw from + withdrawAddr sdk.AccAddress // address to make the withdrawal to +} + +func WithdrawValidatorRewardsAll(operatorAddr, withdrawAddr sdk.AccAddress) + + height = GetHeight() + global = GetGlobal() + pool = GetPool() + ValInfo = GetValidatorDistInfo(delegation.ValidatorAddr) + validator = GetValidator(delegation.ValidatorAddr) + + // withdraw self-delegation + withdraw = GetDelegatorRewardsAll(validator.OperatorAddr, height) + + // withdrawal validator commission rewards + global, commission = valInfo.WithdrawCommission(global, valInfo, height, pool.BondedTokens, + validator.Tokens, validator.Commission) + withdraw += commission + SetGlobal(global) + + AddCoins(withdrawAddr, withdraw.TruncateDecimal()) + +*/ diff --git a/x/distribution/hooks.go b/x/distribution/hooks.go new file mode 100644 index 000000000..689ffabbf --- /dev/null +++ b/x/distribution/hooks.go @@ -0,0 +1,28 @@ +package stake + +/* +## Create or modify delegation distribution + + - triggered-by: `stake.TxDelegate`, `stake.TxBeginRedelegate`, `stake.TxBeginUnbonding` + +The pool of a new delegator bond will be 0 for the height at which the bond was +added, or the withdrawal has taken place. This is achieved by setting +`DelegatorDistInfo.WithdrawalHeight` to the height of the triggering transaction. + +## Commission rate change + + - triggered-by: `stake.TxEditValidator` + +If a validator changes its commission rate, all commission on fees must be +simultaneously withdrawn using the transaction `TxWithdrawValidator`. +Additionally the change and associated height must be recorded in a +`ValidatorUpdate` state record. + +## Change in Validator State + + - triggered-by: `stake.Slash`, `stake.UpdateValidator` + +Whenever a validator is slashed or enters/leaves the validator group all of the +validator entitled reward tokens must be simultaneously withdrawn from +`Global.Pool` and added to `ValidatorDistInfo.Pool`. +*/ diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index b55569d4a..a5d6459ac 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -1 +1,102 @@ package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/stake" + wire "github.com/tendermint/go-wire" +) + +// keeper of the stake store +type Keeper struct { + storeKey sdk.StoreKey + cdc *wire.Codec + coinKeeper bank.Keeper + stakeKeeper stake.Keeper + + // codespace + codespace sdk.CodespaceType +} + +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, + sk stake.Keeper, codespace sdk.CodespaceType) Keeper { + + keeper := Keeper{ + storeKey: key, + cdc: cdc, + coinKeeper: ck, + codespace: codespace, + } + return keeper +} + +//______________________________________________________________________ + +// get the global distribution info +func (k Keeper) GetGlobal(ctx sdk.Context) (global types.Global) { + store := ctx.KVStore(k.storeKey) + + b := store.Get(GlobalKey) + if b == nil { + panic("Stored global should not have been nil") + } + + k.cdc.MustUnmarshalBinary(b, &global) + return +} + +// set the global distribution info +func (k Keeper) SetGlobal(ctx sdk.Context, global types.Global) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinary(global) + store.Set(GlobalKey, b) +} + +//______________________________________________________________________ + +// get the delegator distribution info +func (k Keeper) GetDelegatorDistInfo(ctx sdk.Context, delAddr sdk.AccAddress, + valOperatorAddr sdk.ValAddress) (ddi types.DelegatorDistInfo) { + + store := ctx.KVStore(k.storeKey) + + b := store.Get(GetDelegationDistInfoKey(delAddr, valOperatorAddr)) + if b == nil { + panic("Stored delegation-distribution info should not have been nil") + } + + k.cdc.MustUnmarshalBinary(b, &ddi) + return +} + +// set the delegator distribution info +func (k Keeper) SetDelegatorDistInfo(ctx sdk.Context, ddi types.DelegatorDistInfo) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinary(ddi) + store.Set(GetDelegationDistInfoKey(ddi.DelegatorAddr, ddi.ValOperatorAddr), b) +} + +//______________________________________________________________________ + +// get the validator distribution info +func (k Keeper) GetValidatorDistInfo(ctx sdk.Context, + operatorAddr sdk.ValAddress) (vdi types.ValidatorDistInfo) { + + store := ctx.KVStore(k.storeKey) + + b := store.Get(GetValidatorDistInfoKey(operatorAddr)) + if b == nil { + panic("Stored delegation-distribution info should not have been nil") + } + + k.cdc.MustUnmarshalBinary(b, &vdi) + return +} + +// set the validator distribution info +func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInfo) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinary(vdi) + store.Set(GetValidatorDistInfoKey(vdi.OperatorAddr), b) +} diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index b55569d4a..75200d755 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -1 +1,3 @@ package keeper + +// XXX TODO diff --git a/x/distribution/types/delegator_info.go b/x/distribution/types/delegator_info.go new file mode 100644 index 000000000..55914b59f --- /dev/null +++ b/x/distribution/types/delegator_info.go @@ -0,0 +1,30 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// distribution info for a delegation +type DelegatorDistInfo struct { + DelegatorAddr sdk.AccAddress + ValOperatorAddr sdk.ValAddress + WithdrawalHeight int64 // last time this delegation withdrew rewards +} + +// withdraw rewards from delegator +func (di DelegatorDistInfo) WithdrawRewards(g Global, vi ValidatorDistInfo, + height int64, totalBonded, vdTokens, totalDelShares, commissionRate Dec) ( + di DelegatorDistInfo, g Global, withdrawn DecCoins) { + + vi.UpdateTotalDelAccum(height, totalDelShares) + g = vi.TakeGlobalRewards(g, height, totalBonded, vdTokens, commissionRate) + + blocks = height - di.WithdrawalHeight + di.WithdrawalHeight = height + accum = delegatorShares * blocks + + withdrawalTokens := vi.Pool * accum / vi.TotalDelAccum + vi.TotalDelAccum -= accum + + vi.Pool -= withdrawalTokens + vi.TotalDelAccum -= accum + return di, g, withdrawalTokens +} diff --git a/x/distribution/types/global.go b/x/distribution/types/global.go new file mode 100644 index 000000000..b3b3e2d60 --- /dev/null +++ b/x/distribution/types/global.go @@ -0,0 +1,30 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// coins with decimal +type DecCoins []DecCoin + +// Coins which can have additional decimal points +type DecCoin struct { + Amount sdk.Dec + Denom string +} + +//___________________________________________________________________________________________ + +// Global state for distribution +type Global struct { + TotalValAccumUpdateHeight int64 // last height which the total validator accum was updated + TotalValAccum sdk.Dec // total valdator accum held by validators + Pool DecCoins // funds for all validators which have yet to be withdrawn + CommunityPool DecCoins // pool for community funds yet to be spent +} + +// update total validator accumulation factor +func (g Global) UpdateTotalValAccum(height int64, totalbondedtokens dec) Global { + blocks := height - g.totalvalaccumupdateheight + g.totalvalaccum += totaldelshares * blocks + g.totalvalaccumupdateheight = height + return g +} diff --git a/x/distribution/types/types.go b/x/distribution/types/types.go deleted file mode 100644 index 5a4ca9e2c..000000000 --- a/x/distribution/types/types.go +++ /dev/null @@ -1,35 +0,0 @@ -package types - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// coins with decimal -type DecCoins []DecCoin - -// Coins which can have additional decimal points -type DecCoin struct { - Amount sdk.Dec - Denom string -} - -// Global state for distribution -type Global struct { - TotalValAccumUpdateHeight int64 // last height which the total validator accum was updated - TotalValAccum sdk.Dec // total valdator accum held by validators - Pool DecCoins // funds for all validators which have yet to be withdrawn - CommunityPool DecCoins // pool for community funds yet to be spent -} - -// distribution info for a particular validator -type ValidatorDistInfo struct { - GlobalWithdrawalHeight int64 // last height this validator withdrew from the global pool - Pool DecCoins // rewards owed to delegators, commission has already been charged (includes proposer reward) - PoolCommission DecCoins // commission collected by this validator (pending withdrawal) - - TotalDelAccumUpdateHeight int64 // last height which the total delegator accum was updated - TotalDelAccum sdk.Dec // total proposer pool accumulation factor held by delegators -} - -// distribution info for a delegation -type DelegatorDistInfo struct { - WithdrawalHeight int64 // last time this delegation withdrew rewards -} diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go new file mode 100644 index 000000000..8ca532d86 --- /dev/null +++ b/x/distribution/types/validator_info.go @@ -0,0 +1,57 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// distribution info for a particular validator +type ValidatorDistInfo struct { + OperatorAddr sdk.ValAddress + + GlobalWithdrawalHeight int64 // last height this validator withdrew from the global pool + Pool DecCoins // rewards owed to delegators, commission has already been charged (includes proposer reward) + PoolCommission DecCoins // commission collected by this validator (pending withdrawal) + + TotalDelAccumUpdateHeight int64 // last height which the total delegator accum was updated + TotalDelAccum sdk.Dec // total proposer pool accumulation factor held by delegators +} + +// update total delegator accumululation +func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares Dec) ValidatorDistInfo { + blocks = height - vi.TotalDelAccumUpdateHeight + vi.TotalDelAccum += totalDelShares * blocks + vi.TotalDelAccumUpdateHeight = height + return vi +} + +// move any available accumulated fees in the Global to the validator's pool +func (vi ValidatorDistInfo) TakeGlobalRewards(g Global, height int64, totalBonded, vdTokens, commissionRate Dec) ( + vi ValidatorDistInfo, g Global) { + + g.UpdateTotalValAccum(height, totalBondedShares) + + // update the validators pool + blocks = height - vi.GlobalWithdrawalHeight + vi.GlobalWithdrawalHeight = height + accum = blocks * vdTokens + withdrawalTokens := g.Pool * accum / g.TotalValAccum + commission := withdrawalTokens * commissionRate + + g.TotalValAccum -= accumm + g.Pool -= withdrawalTokens + vi.PoolCommission += commission + vi.PoolCommissionFree += withdrawalTokens - commission + + return vi, g +} + +// withdraw commission rewards +func (vi ValidatorDistInfo) WithdrawCommission(g Global, height int64, + totalBonded, vdTokens, commissionRate Dec) ( + vi ValidatorDistInfo, g Global, withdrawn DecCoins) { + + g = vi.TakeGlobalRewards(g, height, totalBonded, vdTokens, commissionRate) + + withdrawalTokens := vi.PoolCommission + vi.PoolCommission = 0 + + return vi, g, withdrawalTokens +}