diff --git a/docs/spec/staking/old/spec2.md b/docs/spec/staking/old/spec2.md index 72bb8a2e37..3a40b9f449 100644 --- a/docs/spec/staking/old/spec2.md +++ b/docs/spec/staking/old/spec2.md @@ -616,7 +616,7 @@ synced past the height of the oldest `powerChange`. This trim procedure will occur on an epoch basis. ```golang -type powerChange struct { +type powerChange struct height int64 // block height at change power rational.Rat // total power at change prevpower rational.Rat // total power at previous height-1 diff --git a/x/stake/fee_distribution.go b/x/stake/fee_distribution.go index 5078285681..7da08dea17 100644 --- a/x/stake/fee_distribution.go +++ b/x/stake/fee_distribution.go @@ -28,7 +28,6 @@ func (k Keeper) FeeHandler(ctx sdk.Context, collectedFees sdk.Coins) { k.setPool(ctx, pool) } -// XXX need to introduce rat amount based coins for the pool :( func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins { var res sdk.Coins for _, coin := range coins { @@ -38,3 +37,25 @@ func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins { } return res } + +//____________________________________________________________________________- + +// calculate adjustment changes for a candidate at a height +func CalculateAdjustmentChange(candidate Candidate, pool Pool, height int64) (candidate, pool) { + + heightRat := sdk.NewRat(height) + lastHeightRat := sdk.NewRat(height - 1) + candidateFeeCount := candidate.VotingPower.Mul(heightRat) + poolFeeCount := pool.BondedShares.Mul(heightRat) + + // calculate simple and projected pools + simplePool := candidateFeeCount.Quo(poolFeeCount).Mul(pool.SumFeesReceived) + calc1 := candidate.PrevPower.Mul(lastHeightRat).Div(pool.PrevPower.Mul(lastHeightRat)).Mul(pool.PrevFeesReceived) + calc2 := candidate.Power.Div(pool.Power).Mul(pool.RecentFee) + projectedPool := calc1 + calc2 + + AdjustmentChange := simplePool.Sub(projectedPool) + candidate.Adjustment += AdjustmentChange + pool.Adjustment += AdjustmentChange + return candidate, pool +} diff --git a/x/stake/keeper.go b/x/stake/keeper.go index e62e5a3982..0eedd200d6 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -35,9 +35,9 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk. } // get the current in-block validator operation counter -func (k Keeper) getCounter(ctx sdk.Context) int16 { +func (k Keeper) getIntraTxCounter(ctx sdk.Context) int16 { store := ctx.KVStore(k.storeKey) - b := store.Get(CounterKey) + b := store.Get(IntraTxCounterKey) if b == nil { return 0 } @@ -47,10 +47,10 @@ func (k Keeper) getCounter(ctx sdk.Context) int16 { } // set the current in-block validator operation counter -func (k Keeper) setCounter(ctx sdk.Context, counter int16) { +func (k Keeper) setIntraTxCounter(ctx sdk.Context, counter int16) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinary(counter) - store.Set(CounterKey, bz) + store.Set(IntraTxCounterKey, bz) } //_________________________________________________________________________ @@ -143,19 +143,23 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) { // or is a new validator setAcc := false if store.Get(GetRecentValidatorKey(candidate.PubKey)) != nil { - setAcc = true + //setAcc = true - // want to check in the else statement because inefficient - } else if k.isNewValidator(ctx, store, address) { - setAcc = true - } - - if setAcc { bz = k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc)) store.Set(GetAccUpdateValidatorKey(address), bz) + // want to check in the else statement because inefficient + } else if k.isNewValidator(ctx, store, address) { + //setAcc = true + // need to calculate the whole validator set because somebody's gettin' kicked + k.GetValidators(ctx) } + //if setAcc { + //bz = k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc)) + //store.Set(GetAccUpdateValidatorKey(address), bz) + //} + return } @@ -184,6 +188,7 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) { //___________________________________________________________________________ +// XXX NEVER ACTUALLY CALLED ANYWHERE = DETERMINE PLACEMENT // Get the validator set from the candidates. The correct subset is retrieved // by iterating through an index of the candidates sorted by power, stored // using the ValidatorsKey. Simultaniously the most recent the validator @@ -426,6 +431,37 @@ func (k Keeper) removeDelegatorBond(ctx sdk.Context, bond DelegatorBond) { //_______________________________________________________________________ +// XXX TODO trim functionality + +// retrieve all the power changes which occur after a height +func (k Keeper) GetPowerChangesAfterHeight(ctx sdk.Context, earliestHeight int64) (pcs []PowerChange) { + store := ctx.KVStore(k.storeKey) + + iterator := store.SubspaceIterator(PowerChangeKey) //smallest to largest + for ; iterator.Valid(); iterator.Next() { + pcBytes := iterator.Value() + var pc PowerChange + k.cdc.MustUnmarshalBinary(pcBytes, &pc) + if pc.Height < earliestHeight { + break + } + pcs = append(pcs, pc) + } + iterator.Close() + + k.cdc.MustUnmarshalBinary(b, ¶ms) + return +} + +// set a power change +func (k Keeper) setPowerChange(ctx sdk.Context, pc PowerChange) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinary(pc) + store.Set(GetPowerChangeKey(pc.Height), b) +} + +//_______________________________________________________________________ + // load/save the global staking params func (k Keeper) GetParams(ctx sdk.Context) (params Params) { // check if cached before anything diff --git a/x/stake/keeper_keys.go b/x/stake/keeper_keys.go index 263008d761..60e918b7f9 100644 --- a/x/stake/keeper_keys.go +++ b/x/stake/keeper_keys.go @@ -21,7 +21,8 @@ var ( RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond - CounterKey = []byte{0x08} // key for block-local tx index + IntraTxCounterKey = []byte{0x08} // key for block-local tx index + PowerChangeKey = []byte{0x09} // prefix for power change object ) const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch @@ -75,3 +76,10 @@ func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte { } return append(DelegatorBondKeyPrefix, res...) } + +// get the key for the accumulated update validators +func GetPowerChangeKey(height int64) []byte { + heightBytes := make([]byte, binary.MaxVarintLen64) + binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first) + return append(PowerChangeKey, heightBytes...) +} diff --git a/x/stake/tick.go b/x/stake/tick.go index 9ca484061c..2676ec5954 100644 --- a/x/stake/tick.go +++ b/x/stake/tick.go @@ -26,12 +26,18 @@ func (k Keeper) Tick(ctx sdk.Context) (change []abci.Validator) { // save the params k.setPool(ctx, p) - // reset the counter - k.setCounter(ctx, 0) + // reset the intra-transaction counter + k.setIntraTxCounter(ctx, 0) + // calculate validator set changes change = k.getAccUpdateValidators(ctx) + k.clearAccUpdateValidators(ctx) - return + // XXX get the total validator of the previous validator set + // XXX get the total validator of the current validator set + // Calculate the PowerChange term + + return change } // process provisions for an hour period diff --git a/x/stake/types.go b/x/stake/types.go index d06f078d1a..5630387b08 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -77,11 +77,11 @@ type Pool struct { DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily) // XXX need to use special sdk.Rat amounts in coins here because added at small increments - ReservePool sdk.Coins `json:"reserve_pool"` // XXX reserve pool of collected fees for use by governance - FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed - SumFeesReceived sdk.Coins `json:"sum_fees_received"` // XXX sum of all fees received - RecentFee sdk.Coins `json:"recent_fee"` // XXX most recent fee collected - Adjustment sdk.Rat `json:"adjustment"` // XXX Adjustment factor for calculating global fee accum + ReservePool sdk.RatCoin `json:"reserve_pool"` // XXX reserve pool of collected fees for use by governance + FeePool sdk.RatCoin `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed + SumFeesReceived sdk.Coins `json:"sum_fees_received"` // XXX sum of all fees received, post reserve pool + RecentFee sdk.Coins `json:"recent_fee"` // XXX most recent fee collected + Adjustment sdk.Rat `json:"adjustment"` // XXX Adjustment factor for calculating global fee accum } func (p Pool) equal(p2 Pool) bool { @@ -121,6 +121,17 @@ func initialPool() Pool { //_________________________________________________________________________ +// Used in calculation of fee shares, added to a queue for each block where a power change occures +type PowerChange struct { + Height int64 `json:"height"` // block height at change + Power sdk.Rat `json:"power"` // total power at change + PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1 + FeesIn sdk.Coins `json:"fees_in"` // fees in at block height + PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height +} + +//_________________________________________________________________________ + // CandidateStatus - status of a validator-candidate type CandidateStatus byte